source: josm/trunk/src/org/openstreetmap/josm/actions/UploadAction.java@ 6050

Last change on this file since 6050 was 5621, checked in by stoecker, 11 years ago

fix #8211 - data fix on upload

  • Property svn:eol-style set to native
File size: 9.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.awt.event.KeyEvent;
9import java.util.LinkedList;
10
11import javax.swing.JOptionPane;
12import javax.swing.SwingUtilities;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.actions.upload.ApiPreconditionCheckerHook;
16import org.openstreetmap.josm.actions.upload.DiscardTagsHook;
17import org.openstreetmap.josm.actions.upload.FixDataHook;
18import org.openstreetmap.josm.actions.upload.RelationUploadOrderHook;
19import org.openstreetmap.josm.actions.upload.UploadHook;
20import org.openstreetmap.josm.actions.upload.ValidateUploadHook;
21import org.openstreetmap.josm.data.APIDataSet;
22import org.openstreetmap.josm.data.conflict.ConflictCollection;
23import org.openstreetmap.josm.gui.HelpAwareOptionPane;
24import org.openstreetmap.josm.gui.help.HelpUtil;
25import org.openstreetmap.josm.gui.io.UploadDialog;
26import org.openstreetmap.josm.gui.io.UploadPrimitivesTask;
27import org.openstreetmap.josm.gui.layer.OsmDataLayer;
28import org.openstreetmap.josm.gui.util.GuiHelper;
29import org.openstreetmap.josm.tools.ImageProvider;
30import org.openstreetmap.josm.tools.Shortcut;
31
32/**
33 * Action that opens a connection to the osm server and uploads all changes.
34 *
35 * An dialog is displayed asking the user to specify a rectangle to grab.
36 * The url and account settings from the preferences are used.
37 *
38 * If the upload fails this action offers various options to resolve conflicts.
39 *
40 * @author imi
41 */
42public class UploadAction extends JosmAction{
43 /**
44 * The list of upload hooks. These hooks will be called one after the other
45 * when the user wants to upload data. Plugins can insert their own hooks here
46 * if they want to be able to veto an upload.
47 *
48 * Be default, the standard upload dialog is the only element in the list.
49 * Plugins should normally insert their code before that, so that the upload
50 * dialog is the last thing shown before upload really starts; on occasion
51 * however, a plugin might also want to insert something after that.
52 */
53 private static final LinkedList<UploadHook> uploadHooks = new LinkedList<UploadHook>();
54 private static final LinkedList<UploadHook> lateUploadHooks = new LinkedList<UploadHook>();
55 static {
56 /**
57 * Calls validator before upload.
58 */
59 uploadHooks.add(new ValidateUploadHook());
60
61 /**
62 * Fixes database errors
63 */
64 uploadHooks.add(new FixDataHook());
65
66 /**
67 * Checks server capabilities before upload.
68 */
69 uploadHooks.add(new ApiPreconditionCheckerHook());
70
71 /**
72 * Adjusts the upload order of new relations
73 */
74 uploadHooks.add(new RelationUploadOrderHook());
75
76 /**
77 * Removes discardable tags like created_by on modified objects
78 */
79 lateUploadHooks.add(new DiscardTagsHook());
80 }
81
82 /**
83 * Registers an upload hook. Adds the hook at the first position of the upload hooks.
84 *
85 * @param hook the upload hook. Ignored if null.
86 */
87 public static void registerUploadHook(UploadHook hook) {
88 registerUploadHook(hook, false);
89 }
90
91 /**
92 * Registers an upload hook. Adds the hook at the first position of the upload hooks.
93 *
94 * @param hook the upload hook. Ignored if null.
95 * @param late true, if the hook should be executed after the upload dialog
96 * has been confirmed. Late upload hooks should in general succeed and not
97 * abort the upload.
98 */
99 public static void registerUploadHook(UploadHook hook, boolean late) {
100 if(hook == null) return;
101 if (late) {
102 if (!lateUploadHooks.contains(hook)) {
103 lateUploadHooks.add(0, hook);
104 }
105 } else {
106 if (!uploadHooks.contains(hook)) {
107 uploadHooks.add(0, hook);
108 }
109 }
110 }
111
112 /**
113 * Unregisters an upload hook. Removes the hook from the list of upload hooks.
114 *
115 * @param hook the upload hook. Ignored if null.
116 */
117 public static void unregisterUploadHook(UploadHook hook) {
118 if(hook == null) return;
119 if (uploadHooks.contains(hook)) {
120 uploadHooks.remove(hook);
121 }
122 if (lateUploadHooks.contains(hook)) {
123 lateUploadHooks.remove(hook);
124 }
125 }
126
127 public UploadAction() {
128 super(tr("Upload data"), "upload", tr("Upload all changes in the active data layer to the OSM server"),
129 Shortcut.registerShortcut("file:upload", tr("File: {0}", tr("Upload data")), KeyEvent.VK_UP, Shortcut.CTRL_SHIFT), true);
130 putValue("help", ht("/Action/Upload"));
131 }
132
133 /**
134 * Refreshes the enabled state
135 *
136 */
137 @Override
138 protected void updateEnabledState() {
139 setEnabled(getEditLayer() != null);
140 }
141
142 public boolean checkPreUploadConditions(OsmDataLayer layer) {
143 return checkPreUploadConditions(layer, new APIDataSet(layer.data));
144 }
145
146 protected static void alertUnresolvedConflicts(OsmDataLayer layer) {
147 HelpAwareOptionPane.showOptionDialog(
148 Main.parent,
149 tr("<html>The data to be uploaded participates in unresolved conflicts of layer ''{0}''.<br>"
150 + "You have to resolve them first.</html>", layer.getName()
151 ),
152 tr("Warning"),
153 JOptionPane.WARNING_MESSAGE,
154 HelpUtil.ht("/Action/Upload#PrimitivesParticipateInConflicts")
155 );
156 }
157
158 /**
159 * returns true if the user wants to cancel, false if they
160 * want to continue
161 */
162 public static boolean warnUploadDiscouraged(OsmDataLayer layer) {
163 return GuiHelper.warnUser(tr("Upload discouraged"),
164 "<html>" +
165 tr("You are about to upload data from the layer ''{0}''.<br /><br />"+
166 "Sending data from this layer is <b>strongly discouraged</b>. If you continue,<br />"+
167 "it may require you subsequently have to revert your changes, or force other contributors to.<br /><br />"+
168 "Are you sure you want to continue?", layer.getName())+
169 "</html>",
170 ImageProvider.get("upload"), tr("Ignore this hint and upload anyway"));
171 }
172
173 /**
174 * Check whether the preconditions are met to upload data in <code>apiData</code>.
175 * Makes sure upload is allowed, primitives in <code>apiData</code> don't participate in conflicts and
176 * runs the installed {@link UploadHook}s.
177 *
178 * @param layer the source layer of the data to be uploaded
179 * @param apiData the data to be uploaded
180 * @return true, if the preconditions are met; false, otherwise
181 */
182 public boolean checkPreUploadConditions(OsmDataLayer layer, APIDataSet apiData) {
183 if (layer.isUploadDiscouraged()) {
184 if (warnUploadDiscouraged(layer)) {
185 return false;
186 }
187 }
188 ConflictCollection conflicts = layer.getConflicts();
189 if (apiData.participatesInConflict(conflicts)) {
190 alertUnresolvedConflicts(layer);
191 return false;
192 }
193 // Call all upload hooks in sequence.
194 // FIXME: this should become an asynchronous task
195 //
196 for (UploadHook hook : uploadHooks) {
197 if (!hook.checkUpload(apiData))
198 return false;
199 }
200
201 return true;
202 }
203
204 /**
205 * Uploads data to the OSM API.
206 *
207 * @param layer the source layer for the data to upload
208 * @param apiData the primitives to be added, updated, or deleted
209 */
210 public void uploadData(final OsmDataLayer layer, APIDataSet apiData) {
211 if (apiData.isEmpty()) {
212 JOptionPane.showMessageDialog(
213 Main.parent,
214 tr("No changes to upload."),
215 tr("Warning"),
216 JOptionPane.INFORMATION_MESSAGE
217 );
218 return;
219 }
220 if (!checkPreUploadConditions(layer, apiData))
221 return;
222
223 final UploadDialog dialog = UploadDialog.getUploadDialog();
224 // If we simply set the changeset comment here, it would be
225 // overridden by subsequent events in EDT that are caused by
226 // dialog creation. The current solution is to queue this operation
227 // after these events.
228 // TODO: find better way to initialize the comment field
229 SwingUtilities.invokeLater(new Runnable() {
230 public void run() {
231 dialog.setDefaultChangesetTags(layer.data.getChangeSetTags());
232 }
233 });
234 dialog.setUploadedPrimitives(apiData);
235 dialog.setVisible(true);
236 if (dialog.isCanceled())
237 return;
238 dialog.rememberUserInput();
239
240 for (UploadHook hook : lateUploadHooks) {
241 if (!hook.checkUpload(apiData))
242 return;
243 }
244
245 Main.worker.execute(
246 new UploadPrimitivesTask(
247 UploadDialog.getUploadDialog().getUploadStrategySpecification(),
248 layer,
249 apiData,
250 UploadDialog.getUploadDialog().getChangeset()
251 )
252 );
253 }
254
255 public void actionPerformed(ActionEvent e) {
256 if (!isEnabled())
257 return;
258 if (Main.map == null) {
259 JOptionPane.showMessageDialog(
260 Main.parent,
261 tr("Nothing to upload. Get some data first."),
262 tr("Warning"),
263 JOptionPane.WARNING_MESSAGE
264 );
265 return;
266 }
267 APIDataSet apiData = new APIDataSet(Main.main.getCurrentDataSet());
268 uploadData(Main.map.mapView.getEditLayer(), apiData);
269 }
270}
Note: See TracBrowser for help on using the repository browser.