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