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

Last change on this file since 2702 was 2578, checked in by jttt, 14 years ago

Encalupse OsmPrimitive.incomplete

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