001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.event.ActionEvent;
008import java.awt.event.KeyEvent;
009import java.io.IOException;
010import java.util.Collection;
011import java.util.List;
012
013import javax.swing.JOptionPane;
014import javax.swing.SwingUtilities;
015
016import org.openstreetmap.josm.data.osm.Changeset;
017import org.openstreetmap.josm.data.osm.ChangesetCache;
018import org.openstreetmap.josm.data.osm.UserInfo;
019import org.openstreetmap.josm.gui.ExceptionDialogUtil;
020import org.openstreetmap.josm.gui.MainApplication;
021import org.openstreetmap.josm.gui.PleaseWaitRunnable;
022import org.openstreetmap.josm.gui.io.CloseChangesetDialog;
023import org.openstreetmap.josm.gui.io.CloseChangesetTask;
024import org.openstreetmap.josm.io.ChangesetQuery;
025import org.openstreetmap.josm.io.ChangesetUpdater;
026import org.openstreetmap.josm.io.NetworkManager;
027import org.openstreetmap.josm.io.OnlineResource;
028import org.openstreetmap.josm.io.OsmServerChangesetReader;
029import org.openstreetmap.josm.io.OsmServerUserInfoReader;
030import org.openstreetmap.josm.io.OsmTransferException;
031import org.openstreetmap.josm.tools.Shortcut;
032import org.xml.sax.SAXException;
033
034/**
035 * User action to close open changesets.
036 *
037 * The list of open changesets will be downloaded from the server and presented
038 * to the user.
039 */
040public class CloseChangesetAction extends JosmAction {
041
042    /**
043     * Constructs a new {@code CloseChangesetAction}.
044     */
045    public CloseChangesetAction() {
046        super(tr("Close open changesets..."),
047            "closechangeset",
048            tr("Close open changesets"),
049            Shortcut.registerShortcut("system:closechangeset",
050                tr("File: {0}", tr("Close open changesets")),
051                KeyEvent.VK_Q, Shortcut.ALT_CTRL),
052            true
053        );
054        setHelpId(ht("/Action/CloseChangeset"));
055        setEnabled(!NetworkManager.isOffline(OnlineResource.OSM_API));
056
057    }
058
059    @Override
060    public void actionPerformed(ActionEvent e) {
061        MainApplication.worker.submit(new DownloadOpenChangesetsTask());
062    }
063
064    protected void onPostDownloadOpenChangesets() {
065        ChangesetUpdater.check();
066        List<Changeset> openChangesets = ChangesetCache.getInstance().getOpenChangesetsForCurrentUser();
067        if (openChangesets.isEmpty()) {
068            JOptionPane.showMessageDialog(
069                    MainApplication.getMainFrame(),
070                    tr("There are no open changesets"),
071                    tr("No open changesets"),
072                    JOptionPane.INFORMATION_MESSAGE
073            );
074            return;
075        }
076
077        CloseChangesetDialog dialog = new CloseChangesetDialog();
078        dialog.setChangesets(openChangesets);
079        dialog.setVisible(true);
080        if (dialog.isCanceled())
081            return;
082
083        Collection<Changeset> changesetsToClose = dialog.getSelectedChangesets();
084        CloseChangesetTask closeChangesetTask = new CloseChangesetTask(changesetsToClose);
085        MainApplication.worker.submit(closeChangesetTask);
086    }
087
088    private final class DownloadOpenChangesetsTask extends PleaseWaitRunnable {
089
090        private boolean canceled;
091        private OsmServerChangesetReader reader;
092        private List<Changeset> changesets;
093        private Exception lastException;
094
095        private DownloadOpenChangesetsTask() {
096            super(tr("Downloading open changesets ..."), false /* don't ignore exceptions */);
097        }
098
099        @Override
100        protected void cancel() {
101            this.canceled = true;
102            if (reader != null) {
103                reader.cancel();
104            }
105        }
106
107        @Override
108        protected void finish() {
109            SwingUtilities.invokeLater(() -> {
110                            if (lastException != null) {
111                                ExceptionDialogUtil.explainException(lastException);
112                            }
113                            ChangesetCache.getInstance().update(changesets);
114                            if (!canceled && lastException == null) {
115                                onPostDownloadOpenChangesets();
116                            }
117                        });
118        }
119
120        /**
121         * Fetch the user info from the server. This is necessary if we don't know the users id yet
122         *
123         * @return the user info
124         * @throws OsmTransferException in case of any communication exception
125         */
126        private UserInfo fetchUserInfo() throws OsmTransferException {
127            return new OsmServerUserInfoReader().fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false));
128        }
129
130        @Override
131        protected void realRun() throws SAXException, IOException, OsmTransferException {
132            try {
133                UserInfo userInfo = fetchUserInfo();
134                if (canceled)
135                    return;
136                reader = new OsmServerChangesetReader();
137                ChangesetQuery query = new ChangesetQuery().forUser(userInfo.getId()).beingOpen(true);
138                changesets = reader.queryChangesets(
139                        query,
140                        getProgressMonitor().createSubTaskMonitor(1, false /* not internal */)
141                );
142            } catch (OsmTransferException | IllegalArgumentException e) {
143                if (canceled)
144                    return;
145                lastException = e;
146            }
147        }
148
149        /**
150         * Determines if the download task has been canceled.
151         * @return {@code true} if the download task has been canceled
152         */
153        public boolean isCanceled() {
154            return canceled;
155        }
156
157        /**
158         * Returns the last exception that occurred.
159         * @return the last exception that occurred, or {@code null}
160         */
161        public Exception getLastException() {
162            return lastException;
163        }
164    }
165}