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

Last change on this file since 12711 was 12636, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

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