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