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

Last change on this file since 6765 was 6654, checked in by simon04, 10 years ago

fix #9514 fix #9484 fix #9502 - Upload dialog: make source field behave like comment field, provide link "obtain from current layers" to insert current layers in field, display Bing layer as "Bing"

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