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

Last change on this file since 2575 was 2512, checked in by stoecker, 14 years ago

i18n updated, fixed files to reduce problems when applying patches, fix #4017

File size: 13.1 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;
13import java.util.logging.Logger;
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.ExceptionUtil;
34import org.xml.sax.SAXException;
35
36/**
37 * Uploads the current selection to the server.
38 *
39 */
40public class UploadSelectionAction extends JosmAction{
41 static private Logger logger = Logger.getLogger(UploadSelectionAction.class.getName());
42
43 public UploadSelectionAction() {
44 super(
45 tr("Upload selection"),
46 "uploadselection",
47 tr("Upload all changes in the current selection to the OSM server."),
48 null, /* no shortcut */
49 true);
50 putValue("help", ht("/Action/UploadSelection"));
51 }
52
53 @Override
54 protected void updateEnabledState() {
55 if (getCurrentDataSet() == null) {
56 setEnabled(false);
57 } else {
58 updateEnabledState(getCurrentDataSet().getSelected());
59 }
60 }
61
62 @Override
63 protected void updateEnabledState(Collection<? extends OsmPrimitive> selection) {
64 setEnabled(selection != null && !selection.isEmpty());
65 }
66
67 protected Set<OsmPrimitive> getDeletedPrimitives(DataSet ds) {
68 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
69 for (OsmPrimitive p : ds.getNodes()) {
70 if (p.isDeleted() && !p.isNew() && p.isVisible() && p.isModified()) {
71 ret.add(p);
72 }
73 }
74 for (OsmPrimitive p : ds.getWays()) {
75 if (p.isDeleted() && !p.isNew() && p.isVisible() && p.isModified()) {
76 ret.add(p);
77 }
78 }
79 for (OsmPrimitive p : ds.getRelations()) {
80 if (p.isDeleted() && !p.isNew() && p.isVisible() && p.isModified()) {
81 ret.add(p);
82 }
83 }
84 return ret;
85 }
86
87 protected Set<OsmPrimitive> getModifiedPrimitives(Collection<OsmPrimitive> primitives) {
88 HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
89 for (OsmPrimitive p: primitives) {
90 if (p.isNew()) {
91 ret.add(p);
92 } else if (p.isVisible() && p.isModified() && !p.incomplete) {
93 ret.add(p);
94 }
95 }
96 return ret;
97 }
98
99 public void actionPerformed(ActionEvent e) {
100 if (!isEnabled())
101 return;
102 UploadHullBuilder builder = new UploadHullBuilder();
103 UploadSelectionDialog dialog = new UploadSelectionDialog();
104 Collection<OsmPrimitive> modifiedCandidates = getModifiedPrimitives(getEditLayer().data.getSelected());
105 Collection<OsmPrimitive> deletedCandiates = getDeletedPrimitives(getEditLayer().data);
106 if (modifiedCandidates.isEmpty() && deletedCandiates.isEmpty()) {
107 JOptionPane.showMessageDialog(
108 Main.parent,
109 tr("No changes to upload."),
110 tr("Warning"),
111 JOptionPane.INFORMATION_MESSAGE
112 );
113 return;
114 }
115 dialog.populate(
116 modifiedCandidates,
117 deletedCandiates
118 );
119 dialog.setVisible(true);
120 if (dialog.isCanceled())
121 return;
122 Collection<OsmPrimitive> toUpload = builder.build(dialog.getSelectedPrimitives());
123 if (toUpload.isEmpty()) {
124 JOptionPane.showMessageDialog(
125 Main.parent,
126 tr("No changes to upload."),
127 tr("Warning"),
128 JOptionPane.INFORMATION_MESSAGE
129 );
130 return;
131 }
132 uploadPrimitives(getEditLayer(), toUpload);
133 }
134
135 protected boolean hasPrimitivesToDelete(Collection<OsmPrimitive> primitives) {
136 for (OsmPrimitive p: primitives)
137 if (p.isDeleted() && p.isModified() && !p.isNew())
138 return true;
139 return false;
140 }
141 /**
142 * Uploads the primitives in <code>toUpload</code> to the server. Only
143 * uploads primitives which are either new, modified or deleted.
144 *
145 * Also checks whether <code>toUpload</code> has to be extended with
146 * deleted parents in order to avoid precondition violations on the server.
147 *
148 * @param layer the data layer from which we upload a subset of primitives
149 * @param toUpload the primitives to upload. If null or empty returns immediatelly
150 */
151 public void uploadPrimitives(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
152 if (toUpload == null || toUpload.isEmpty()) return;
153 UploadHullBuilder builder = new UploadHullBuilder();
154 toUpload = builder.build(toUpload);
155 if (hasPrimitivesToDelete(toUpload)) {
156 // runs the check for deleted parents and then invokes
157 // processPostParentChecker()
158 //
159 Main.worker.submit(new DeletedParentsChecker(layer, toUpload));
160 } else {
161 processPostParentChecker(layer, toUpload);
162 }
163 }
164
165 protected void processPostParentChecker(OsmDataLayer layer, Collection<OsmPrimitive> toUpload) {
166 APIDataSet ds = new APIDataSet(toUpload);
167 UploadAction action = new UploadAction();
168 action.uploadData(layer, ds);
169 }
170
171 /**
172 * Computes the collection of primitives to upload, given a collection of candidate
173 * primitives.
174 * Some of the candidates are excluded, i.e. if they aren't modified or if they
175 * aren't visible.
176 * Other primitives are added. A typical case is a primitive which is new and and
177 * which is referred by a modified relation. In order to upload the relation the
178 * new primitive has to be uploaded as well, even if it isn't included in the
179 * list of candidate primitives.
180 *
181 */
182 static class UploadHullBuilder implements Visitor {
183 private Set<OsmPrimitive> hull;
184
185 public UploadHullBuilder(){
186 hull = new HashSet<OsmPrimitive>();
187 }
188
189 public void visit(Node n) {
190 if (n.isNew() || ((n.isModified() || n.isDeleted()) && n.isVisible())) {
191 // upload new nodes as well as modified and deleted ones
192 hull.add(n);
193 }
194 }
195
196 public void visit(Way w) {
197 if (w.isNew() || ((w.isModified() || w.isDeleted()) && w.isVisible())) {
198 // upload new ways as well as modified and deleted ones
199 hull.add(w);
200 for (Node n: w.getNodes()) {
201 // we upload modified nodes even if they aren't in the current
202 // selection.
203 n.visit(this);
204 }
205 }
206 }
207
208 public void visit(Relation r) {
209 if (r.isNew() || ((r.isModified() || r.isDeleted()) && r.isVisible())) {
210 hull.add(r);
211 for (OsmPrimitive p : r.getMemberPrimitives()) {
212 // add new relation members. Don't include modified
213 // relation members. r shouldn't refer to deleted primitives,
214 // so wont check here for deleted primitives here
215 //
216 if (p.isNew()) {
217 p.visit(this);
218 }
219 }
220 }
221 }
222
223 public void visit(Changeset cs) {
224 // do nothing
225 }
226
227 /**
228 * Builds the "hull" of primitives to be uploaded given a base collection
229 * of osm primitives.
230 *
231 * @param base the base collection. Must not be null.
232 * @return the "hull"
233 * @throws IllegalArgumentException thrown if base is null
234 */
235 public Set<OsmPrimitive> build(Collection<OsmPrimitive> base) throws IllegalArgumentException{
236 if (base == null)
237 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null", "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.isNew()) {
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.