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

Last change on this file since 5025 was 5025, checked in by Don-vip, 13 years ago

see #4043 - Have an 'upload prohibited' flag in .osm files

  • Property svn:eol-style set to native
File size: 13.0 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 if (getEditLayer().isUploadDiscouraged()) {
91 if (UploadAction.warnUploadDiscouraged(getEditLayer())) {
92 return;
93 }
94 }
95 UploadHullBuilder builder = new UploadHullBuilder();
96 UploadSelectionDialog dialog = new UploadSelectionDialog();
97 Collection<OsmPrimitive> modifiedCandidates = getModifiedPrimitives(getEditLayer().data.getSelected());
98 Collection<OsmPrimitive> deletedCandidates = getDeletedPrimitives(getEditLayer().data);
99 if (modifiedCandidates.isEmpty() && deletedCandidates.isEmpty()) {
100 JOptionPane.showMessageDialog(
101 Main.parent,
102 tr("No changes to upload."),
103 tr("Warning"),
104 JOptionPane.INFORMATION_MESSAGE
105 );
106 return;
107 }
108 dialog.populate(
109 modifiedCandidates,
110 deletedCandidates
111 );
112 dialog.setVisible(true);
113 if (dialog.isCanceled())
114 return;
115 Collection<OsmPrimitive> toUpload = builder.build(dialog.getSelectedPrimitives());
116 if (toUpload.isEmpty()) {
117 JOptionPane.showMessageDialog(
118 Main.parent,
119 tr("No changes to upload."),
120 tr("Warning"),
121 JOptionPane.INFORMATION_MESSAGE
122 );
123 return;
124 }
125 uploadPrimitives(getEditLayer(), toUpload);
126 }
127
128 /**
129 * Replies true if there is at least one non-new, deleted primitive in
130 * <code>primitives</code>
131 *
132 * @param primitives the primitives to scan
133 * @return true if there is at least one non-new, deleted primitive in
134 * <code>primitives</code>
135 */
136 protected boolean hasPrimitivesToDelete(Collection<OsmPrimitive> primitives) {
137 for (OsmPrimitive p: primitives)
138 if (p.isDeleted() && p.isModified() && !p.isNew())
139 return true;
140 return false;
141 }
142
143 /**
144 * Uploads the primitives in <code>toUpload</code> to the server. Only
145 * uploads primitives which are either new, modified or deleted.
146 *
147 * Also checks whether <code>toUpload</code> has to be extended with
148 * deleted parents in order to avoid precondition violations on the server.
149 *
150 * @param layer the data layer from which we upload a subset of primitives
151 * @param toUpload the primitives to upload. If null or empty returns immediatelly
152 */
153 public void uploadPrimitives(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
154 if (toUpload == null || toUpload.isEmpty()) return;
155 UploadHullBuilder builder = new UploadHullBuilder();
156 toUpload = builder.build(toUpload);
157 if (hasPrimitivesToDelete(toUpload)) {
158 // runs the check for deleted parents and then invokes
159 // processPostParentChecker()
160 //
161 Main.worker.submit(new DeletedParentsChecker(layer, toUpload));
162 } else {
163 processPostParentChecker(layer, toUpload);
164 }
165 }
166
167 protected void processPostParentChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
168 APIDataSet ds = new APIDataSet(toUpload);
169 UploadAction action = new UploadAction();
170 action.uploadData(layer, ds);
171 }
172
173 /**
174 * Computes the collection of primitives to upload, given a collection of candidate
175 * primitives.
176 * Some of the candidates are excluded, i.e. if they aren't modified.
177 * Other primitives are added. A typical case is a primitive which is new and and
178 * which is referred by a modified relation. In order to upload the relation the
179 * new primitive has to be uploaded as well, even if it isn't included in the
180 * list of candidate primitives.
181 *
182 */
183 static class UploadHullBuilder implements Visitor {
184 private Set<OsmPrimitive> hull;
185
186 public UploadHullBuilder(){
187 hull = new HashSet<OsmPrimitive>();
188 }
189
190 public void visit(Node n) {
191 if (n.isNewOrUndeleted() || n.isModified() || n.isDeleted()) {
192 // upload new nodes as well as modified and deleted ones
193 hull.add(n);
194 }
195 }
196
197 public void visit(Way w) {
198 if (w.isNewOrUndeleted() || w.isModified() || w.isDeleted()) {
199 // upload new ways as well as modified and deleted ones
200 hull.add(w);
201 for (Node n: w.getNodes()) {
202 // we upload modified nodes even if they aren't in the current
203 // selection.
204 n.visit(this);
205 }
206 }
207 }
208
209 public void visit(Relation r) {
210 if (r.isNewOrUndeleted() || r.isModified() || r.isDeleted()) {
211 hull.add(r);
212 for (OsmPrimitive p : r.getMemberPrimitives()) {
213 // add new relation members. Don't include modified
214 // relation members. r shouldn't refer to deleted primitives,
215 // so wont check here for deleted primitives here
216 //
217 if (p.isNewOrUndeleted()) {
218 p.visit(this);
219 }
220 }
221 }
222 }
223
224 public void visit(Changeset cs) {
225 // do nothing
226 }
227
228 /**
229 * Builds the "hull" of primitives to be uploaded given a base collection
230 * of osm primitives.
231 *
232 * @param base the base collection. Must not be null.
233 * @return the "hull"
234 * @throws IllegalArgumentException thrown if base is null
235 */
236 public Set<OsmPrimitive> build(Collection<OsmPrimitive> base) throws IllegalArgumentException{
237 CheckParameterUtil.ensureParameterNotNull(base, "base");
238 hull = new HashSet<OsmPrimitive>();
239 for (OsmPrimitive p: base) {
240 p.visit(this);
241 }
242 return hull;
243 }
244 }
245
246 class DeletedParentsChecker extends PleaseWaitRunnable {
247 private boolean canceled;
248 private Exception lastException;
249 private Collection<OsmPrimitive> toUpload;
250 private OsmDataLayer layer;
251 private OsmServerBackreferenceReader reader;
252
253 /**
254 *
255 * @param layer the data layer for which a collection of selected primitives is uploaded
256 * @param toUpload the collection of primitives to upload
257 */
258 public DeletedParentsChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
259 super(tr("Checking parents for deleted objects"));
260 this.toUpload = toUpload;
261 this.layer = layer;
262 }
263
264 @Override
265 protected void cancel() {
266 this.canceled = true;
267 synchronized (this) {
268 if (reader != null) {
269 reader.cancel();
270 }
271 }
272 }
273
274 @Override
275 protected void finish() {
276 if (canceled)
277 return;
278 if (lastException != null) {
279 ExceptionUtil.explainException(lastException);
280 return;
281 }
282 Runnable r = new Runnable() {
283 public void run() {
284 processPostParentChecker(layer, toUpload);
285 }
286 };
287 SwingUtilities.invokeLater(r);
288 }
289
290 /**
291 * Replies the collection of deleted OSM primitives for which we have to check whether
292 * there are dangling references on the server.
293 *
294 * @return
295 */
296 protected Set<OsmPrimitive> getPrimitivesToCheckForParents() {
297 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
298 for (OsmPrimitive p: toUpload) {
299 if (p.isDeleted() && !p.isNewOrUndeleted()) {
300 ret.add(p);
301 }
302 }
303 return ret;
304 }
305
306 @Override
307 protected void realRun() throws SAXException, IOException, OsmTransferException {
308 try {
309 Stack<OsmPrimitive> toCheck = new Stack<OsmPrimitive>();
310 toCheck.addAll(getPrimitivesToCheckForParents());
311 Set<OsmPrimitive> checked = new HashSet<OsmPrimitive>();
312 while(!toCheck.isEmpty()) {
313 if (canceled) return;
314 OsmPrimitive current = toCheck.pop();
315 synchronized(this) {
316 reader = new OsmServerBackreferenceReader(current);
317 }
318 getProgressMonitor().subTask(tr("Reading parents of ''{0}''", current.getDisplayName(DefaultNameFormatter.getInstance())));
319 DataSet ds = reader.parseOsm(getProgressMonitor().createSubTaskMonitor(1, false));
320 synchronized(this) {
321 reader = null;
322 }
323 checked.add(current);
324 getProgressMonitor().subTask(tr("Checking for deleted parents in the local dataset"));
325 for (OsmPrimitive p: ds.allPrimitives()) {
326 if (canceled) return;
327 OsmPrimitive myDeletedParent = layer.data.getPrimitiveById(p);
328 // our local dataset includes a deleted parent of a primitive we want
329 // to delete. Include this parent in the collection of uploaded primitives
330 //
331 if (myDeletedParent != null && myDeletedParent.isDeleted()) {
332 if (!toUpload.contains(myDeletedParent)) {
333 toUpload.add(myDeletedParent);
334 }
335 if (!checked.contains(myDeletedParent)) {
336 toCheck.push(myDeletedParent);
337 }
338 }
339 }
340 }
341 } catch(Exception e) {
342 if (canceled)
343 // ignore exception
344 return;
345 lastException = e;
346 }
347 }
348 }
349}
Note: See TracBrowser for help on using the repository browser.