source: josm/trunk/src/org/openstreetmap/josm/actions/UploadSelectionAction.java@ 4851

Last change on this file since 4851 was 4191, checked in by stoecker, 13 years ago

remove old debug stuff

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.event.ActionEvent;
8import java.io.IOException;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.Set;
12import java.util.Stack;
13
14import javax.swing.JOptionPane;
15import javax.swing.SwingUtilities;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.APIDataSet;
19import org.openstreetmap.josm.data.osm.Changeset;
20import org.openstreetmap.josm.data.osm.DataSet;
21import org.openstreetmap.josm.data.osm.Node;
22import org.openstreetmap.josm.data.osm.OsmPrimitive;
23import org.openstreetmap.josm.data.osm.Relation;
24import org.openstreetmap.josm.data.osm.Way;
25import org.openstreetmap.josm.data.osm.visitor.Visitor;
26import org.openstreetmap.josm.gui.DefaultNameFormatter;
27import org.openstreetmap.josm.gui.PleaseWaitRunnable;
28import org.openstreetmap.josm.gui.io.UploadSelectionDialog;
29import org.openstreetmap.josm.gui.layer.OsmDataLayer;
30import org.openstreetmap.josm.io.OsmServerBackreferenceReader;
31import org.openstreetmap.josm.io.OsmTransferException;
32import org.openstreetmap.josm.tools.CheckParameterUtil;
33import org.openstreetmap.josm.tools.ExceptionUtil;
34import org.xml.sax.SAXException;
35
36/**
37 * Uploads the current selection to the server.
38 *
39 */
40public class UploadSelectionAction extends JosmAction{
41 public UploadSelectionAction() {
42 super(
43 tr("Upload selection"),
44 "uploadselection",
45 tr("Upload all changes in the current selection to the OSM server."),
46 null, /* no shortcut */
47 true);
48 putValue("help", ht("/Action/UploadSelection"));
49 }
50
51 @Override
52 protected void updateEnabledState() {
53 if (getCurrentDataSet() == null) {
54 setEnabled(false);
55 } else {
56 updateEnabledState(getCurrentDataSet().getSelected());
57 }
58 }
59
60 @Override
61 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
62 setEnabled(selection != null && !selection.isEmpty());
63 }
64
65 protected Set<OsmPrimitive> getDeletedPrimitives(DataSet ds) {
66 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
67 for (OsmPrimitive p: ds.allPrimitives()) {
68 if (p.isDeleted() && !p.isNew() && p.isVisible() && p.isModified()) {
69 ret.add(p);
70 }
71 }
72 return ret;
73 }
74
75 protected Set<OsmPrimitive> getModifiedPrimitives(Collection<OsmPrimitive> primitives) {
76 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
77 for (OsmPrimitive p: primitives) {
78 if (p.isNewOrUndeleted()) {
79 ret.add(p);
80 } else if (p.isModified() && !p.isIncomplete()) {
81 ret.add(p);
82 }
83 }
84 return ret;
85 }
86
87 public void actionPerformed(ActionEvent e) {
88 if (!isEnabled())
89 return;
90 UploadHullBuilder builder = new UploadHullBuilder();
91 UploadSelectionDialog dialog = new UploadSelectionDialog();
92 Collection<OsmPrimitive> modifiedCandidates = getModifiedPrimitives(getEditLayer().data.getSelected());
93 Collection<OsmPrimitive> deletedCandidates = getDeletedPrimitives(getEditLayer().data);
94 if (modifiedCandidates.isEmpty() && deletedCandidates.isEmpty()) {
95 JOptionPane.showMessageDialog(
96 Main.parent,
97 tr("No changes to upload."),
98 tr("Warning"),
99 JOptionPane.INFORMATION_MESSAGE
100 );
101 return;
102 }
103 dialog.populate(
104 modifiedCandidates,
105 deletedCandidates
106 );
107 dialog.setVisible(true);
108 if (dialog.isCanceled())
109 return;
110 Collection<OsmPrimitive> toUpload = builder.build(dialog.getSelectedPrimitives());
111 if (toUpload.isEmpty()) {
112 JOptionPane.showMessageDialog(
113 Main.parent,
114 tr("No changes to upload."),
115 tr("Warning"),
116 JOptionPane.INFORMATION_MESSAGE
117 );
118 return;
119 }
120 uploadPrimitives(getEditLayer(), toUpload);
121 }
122
123 /**
124 * Replies true if there is at least one non-new, deleted primitive in
125 * <code>primitives</code>
126 *
127 * @param primitives the primitives to scan
128 * @return true if there is at least one non-new, deleted primitive in
129 * <code>primitives</code>
130 */
131 protected boolean hasPrimitivesToDelete(Collection<OsmPrimitive> primitives) {
132 for (OsmPrimitive p: primitives)
133 if (p.isDeleted() && p.isModified() && !p.isNew())
134 return true;
135 return false;
136 }
137
138 /**
139 * Uploads the primitives in <code>toUpload</code> to the server. Only
140 * uploads primitives which are either new, modified or deleted.
141 *
142 * Also checks whether <code>toUpload</code> has to be extended with
143 * deleted parents in order to avoid precondition violations on the server.
144 *
145 * @param layer the data layer from which we upload a subset of primitives
146 * @param toUpload the primitives to upload. If null or empty returns immediatelly
147 */
148 public void uploadPrimitives(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
149 if (toUpload == null || toUpload.isEmpty()) return;
150 UploadHullBuilder builder = new UploadHullBuilder();
151 toUpload = builder.build(toUpload);
152 if (hasPrimitivesToDelete(toUpload)) {
153 // runs the check for deleted parents and then invokes
154 // processPostParentChecker()
155 //
156 Main.worker.submit(new DeletedParentsChecker(layer, toUpload));
157 } else {
158 processPostParentChecker(layer, toUpload);
159 }
160 }
161
162 protected void processPostParentChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
163 APIDataSet ds = new APIDataSet(toUpload);
164 UploadAction action = new UploadAction();
165 action.uploadData(layer, ds);
166 }
167
168 /**
169 * Computes the collection of primitives to upload, given a collection of candidate
170 * primitives.
171 * Some of the candidates are excluded, i.e. if they aren't modified.
172 * Other primitives are added. A typical case is a primitive which is new and and
173 * which is referred by a modified relation. In order to upload the relation the
174 * new primitive has to be uploaded as well, even if it isn't included in the
175 * list of candidate primitives.
176 *
177 */
178 static class UploadHullBuilder implements Visitor {
179 private Set<OsmPrimitive> hull;
180
181 public UploadHullBuilder(){
182 hull = new HashSet<OsmPrimitive>();
183 }
184
185 public void visit(Node n) {
186 if (n.isNewOrUndeleted() || n.isModified() || n.isDeleted()) {
187 // upload new nodes as well as modified and deleted ones
188 hull.add(n);
189 }
190 }
191
192 public void visit(Way w) {
193 if (w.isNewOrUndeleted() || w.isModified() || w.isDeleted()) {
194 // upload new ways as well as modified and deleted ones
195 hull.add(w);
196 for (Node n: w.getNodes()) {
197 // we upload modified nodes even if they aren't in the current
198 // selection.
199 n.visit(this);
200 }
201 }
202 }
203
204 public void visit(Relation r) {
205 if (r.isNewOrUndeleted() || r.isModified() || r.isDeleted()) {
206 hull.add(r);
207 for (OsmPrimitive p : r.getMemberPrimitives()) {
208 // add new relation members. Don't include modified
209 // relation members. r shouldn't refer to deleted primitives,
210 // so wont check here for deleted primitives here
211 //
212 if (p.isNewOrUndeleted()) {
213 p.visit(this);
214 }
215 }
216 }
217 }
218
219 public void visit(Changeset cs) {
220 // do nothing
221 }
222
223 /**
224 * Builds the "hull" of primitives to be uploaded given a base collection
225 * of osm primitives.
226 *
227 * @param base the base collection. Must not be null.
228 * @return the "hull"
229 * @throws IllegalArgumentException thrown if base is null
230 */
231 public Set<OsmPrimitive> build(Collection<OsmPrimitive> base) throws IllegalArgumentException{
232 CheckParameterUtil.ensureParameterNotNull(base, "base");
233 hull = new HashSet<OsmPrimitive>();
234 for (OsmPrimitive p: base) {
235 p.visit(this);
236 }
237 return hull;
238 }
239 }
240
241 class DeletedParentsChecker extends PleaseWaitRunnable {
242 private boolean canceled;
243 private Exception lastException;
244 private Collection<OsmPrimitive> toUpload;
245 private OsmDataLayer layer;
246 private OsmServerBackreferenceReader reader;
247
248 /**
249 *
250 * @param layer the data layer for which a collection of selected primitives is uploaded
251 * @param toUpload the collection of primitives to upload
252 */
253 public DeletedParentsChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
254 super(tr("Checking parents for deleted objects"));
255 this.toUpload = toUpload;
256 this.layer = layer;
257 }
258
259 @Override
260 protected void cancel() {
261 this.canceled = true;
262 synchronized (this) {
263 if (reader != null) {
264 reader.cancel();
265 }
266 }
267 }
268
269 @Override
270 protected void finish() {
271 if (canceled)
272 return;
273 if (lastException != null) {
274 ExceptionUtil.explainException(lastException);
275 return;
276 }
277 Runnable r = new Runnable() {
278 public void run() {
279 processPostParentChecker(layer, toUpload);
280 }
281 };
282 SwingUtilities.invokeLater(r);
283 }
284
285 /**
286 * Replies the collection of deleted OSM primitives for which we have to check whether
287 * there are dangling references on the server.
288 *
289 * @return
290 */
291 protected Set<OsmPrimitive> getPrimitivesToCheckForParents() {
292 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
293 for (OsmPrimitive p: toUpload) {
294 if (p.isDeleted() && !p.isNewOrUndeleted()) {
295 ret.add(p);
296 }
297 }
298 return ret;
299 }
300
301 @Override
302 protected void realRun() throws SAXException, IOException, OsmTransferException {
303 try {
304 Stack<OsmPrimitive> toCheck = new Stack<OsmPrimitive>();
305 toCheck.addAll(getPrimitivesToCheckForParents());
306 Set<OsmPrimitive> checked = new HashSet<OsmPrimitive>();
307 while(!toCheck.isEmpty()) {
308 if (canceled) return;
309 OsmPrimitive current = toCheck.pop();
310 synchronized(this) {
311 reader = new OsmServerBackreferenceReader(current);
312 }
313 getProgressMonitor().subTask(tr("Reading parents of ''{0}''", current.getDisplayName(DefaultNameFormatter.getInstance())));
314 DataSet ds = reader.parseOsm(getProgressMonitor().createSubTaskMonitor(1, false));
315 synchronized(this) {
316 reader = null;
317 }
318 checked.add(current);
319 getProgressMonitor().subTask(tr("Checking for deleted parents in the local dataset"));
320 for (OsmPrimitive p: ds.allPrimitives()) {
321 if (canceled) return;
322 OsmPrimitive myDeletedParent = layer.data.getPrimitiveById(p);
323 // our local dataset includes a deleted parent of a primitive we want
324 // to delete. Include this parent in the collection of uploaded primitives
325 //
326 if (myDeletedParent != null && myDeletedParent.isDeleted()) {
327 if (!toUpload.contains(myDeletedParent)) {
328 toUpload.add(myDeletedParent);
329 }
330 if (!checked.contains(myDeletedParent)) {
331 toCheck.push(myDeletedParent);
332 }
333 }
334 }
335 }
336 } catch(Exception e) {
337 if (canceled)
338 // ignore exception
339 return;
340 lastException = e;
341 }
342 }
343 }
344}
Note: See TracBrowser for help on using the repository browser.