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

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

update license/copyright information

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