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

Revision 5233, 8.4 KB checked in by Don-vip, 9 days ago (diff)

fix #7684 - Various improvements in "upload=false" layers handling

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