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

Last change on this file since 8461 was 8338, checked in by Don-vip, 9 years ago

fix squid:S1319 - Declarations should use Java collection interfaces rather than specific implementation classes

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