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