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

Last change on this file since 14159 was 14153, checked in by Don-vip, 6 years ago

see #15229 - deprecate Main.parent and Main itself

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