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

Last change on this file since 7448 was 7402, checked in by Don-vip, 10 years ago

fix some Sonar issues

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