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

Last change on this file since 5497 was 5497, checked in by bastiK, 12 years ago

applied #7915 - Automatically discard some TIGER tags on upload (based on patch by ToeBee)

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