001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.GraphicsEnvironment;
008import java.io.IOException;
009import java.util.Collections;
010import java.util.List;
011
012import javax.swing.JOptionPane;
013import javax.swing.SwingUtilities;
014
015import org.openstreetmap.josm.Main;
016import org.openstreetmap.josm.data.UserIdentityManager;
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.PleaseWaitRunnable;
022import org.openstreetmap.josm.gui.util.GuiHelper;
023import org.openstreetmap.josm.io.ChangesetQuery;
024import org.openstreetmap.josm.io.OsmServerChangesetReader;
025import org.openstreetmap.josm.io.OsmServerUserInfoReader;
026import org.openstreetmap.josm.io.OsmTransferException;
027import org.openstreetmap.josm.tools.Logging;
028import org.xml.sax.SAXException;
029
030/**
031 * This is a task for downloading the open changesets of the current user
032 * from the OSM server.
033 */
034public class DownloadOpenChangesetsTask extends PleaseWaitRunnable {
035
036    private boolean canceled;
037    private OsmServerChangesetReader reader;
038    private List<Changeset> changesets;
039    private Exception lastException;
040    private final Component parent;
041
042    /**
043     * Constructs the task
044     * @param parent is a component to show error messages
045     */
046    public DownloadOpenChangesetsTask(Component parent) {
047        super(parent, tr("Downloading open changesets ..."), false /* don't ignore exceptions */);
048        this.parent = parent;
049    }
050
051    @Override
052    protected void cancel() {
053        this.canceled = true;
054        synchronized (this) {
055            if (reader != null) {
056                reader.cancel();
057            }
058        }
059    }
060
061    @Override
062    protected void finish() {
063        if (UserIdentityManager.getInstance().isAnonymous()) {
064            String msg = tr("Could not retrieve the list of your open changesets because<br>"
065                    + "JOSM does not know your identity.<br>"
066                    + "You have either chosen to work anonymously or you are not entitled<br>"
067                    + "to know the identity of the user on whose behalf you are working.");
068            Logging.warn(msg);
069            if (!GraphicsEnvironment.isHeadless()) {
070                JOptionPane.showMessageDialog(GuiHelper.getFrameForComponent(parent),
071                        "<html>" + msg + "</html>", tr("Missing user identity"), JOptionPane.ERROR_MESSAGE);
072            }
073            return;
074        }
075        if (canceled) return;
076        if (lastException != null) {
077            ExceptionDialogUtil.explainException(lastException);
078            return;
079        }
080        if (changesets.isEmpty()) {
081            if (!GraphicsEnvironment.isHeadless()) {
082                JOptionPane.showMessageDialog(
083                        Main.parent,
084                        tr("There are no open changesets"),
085                        tr("No open changesets"),
086                        JOptionPane.INFORMATION_MESSAGE
087                );
088            }
089            return;
090        }
091        SwingUtilities.invokeLater(() -> ChangesetCache.getInstance().update(changesets));
092    }
093
094    /**
095     * Refreshes the user info from the server. This is necessary if we don't know the users id yet.
096     */
097    protected void refreshUserIdentity() {
098        UserIdentityManager im = UserIdentityManager.getInstance();
099        try {
100            OsmServerUserInfoReader infoReader = new OsmServerUserInfoReader();
101            UserInfo info = infoReader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false));
102            im.setFullyIdentified(info.getDisplayName(), info);
103        } catch (OsmTransferException e) {
104            // retrieving the user info can fail if the current user is not authorised to
105            // retrieve it, i.e. if he is working with an OAuth Access Token which doesn't
106            // have the respective privileges or if he didn't or he can't authenticate with
107            // a username/password-pair.
108            //
109            // Downgrade your knowlege about its identity if we've assumed that he was fully
110            // identified. Otherwise, if he is anonymous or partially identified, keep our level
111            // of knowlege.
112            //
113            if (im.isFullyIdentified()) {
114                im.setPartiallyIdentified(im.getUserName());
115            }
116            Logging.log(Logging.LEVEL_WARN,
117                    tr("Failed to retrieve user infos for the current JOSM user. Exception was: {0}", e.toString()), e);
118        }
119    }
120
121    @Override
122    protected void realRun() throws SAXException, IOException, OsmTransferException {
123        try {
124            UserIdentityManager im = UserIdentityManager.getInstance();
125            if (im.isAnonymous()) {
126                refreshUserIdentity();
127            } else if (im.isFullyIdentified()) {
128                // do nothing
129            } else if (im.isPartiallyIdentified()) {
130                refreshUserIdentity();
131            }
132            if (canceled) return;
133            synchronized (this) {
134                reader = new OsmServerChangesetReader();
135            }
136            ChangesetQuery query = new ChangesetQuery().beingOpen(true);
137            if (im.isAnonymous())
138                // we still don't know anything about the current user. Can't retrieve
139                // its changesets
140                return;
141            else if (im.isFullyIdentified()) {
142                query = query.forUser(im.getUserId());
143            } else {
144                // we only know the users name, not its id. Nevermind, try to read
145                // its open changesets anyway.
146                //
147                query = query.forUser(im.getUserName());
148            }
149            changesets = reader.queryChangesets(
150                    query,
151                    getProgressMonitor().createSubTaskMonitor(1, false /* not internal */)
152            );
153        } catch (OsmTransferException e) {
154            if (canceled)
155                return;
156            lastException = e;
157        }
158    }
159
160    /**
161     * Determines if this task has been cancelled.
162     * @return {@code true} if this task has been cancelled
163     */
164    public boolean isCanceled() {
165        return canceled;
166    }
167
168    /**
169     * Returns the changesets.
170     * @return the changesets, or {@code null}
171     * @since 11110
172     */
173    public final List<Changeset> getChangesets() {
174        return changesets != null ? Collections.unmodifiableList(changesets) : null;
175    }
176}