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

Last change on this file since 10420 was 10382, checked in by Don-vip, 8 years ago

see #12943 - gsoc-core - fix most of deprecation warnings (static accesses must be fixed)

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