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.io.IOException;
008import java.util.Collections;
009import java.util.List;
010
011import javax.swing.JOptionPane;
012import javax.swing.SwingUtilities;
013
014import org.openstreetmap.josm.data.UserIdentityManager;
015import org.openstreetmap.josm.data.osm.Changeset;
016import org.openstreetmap.josm.data.osm.ChangesetCache;
017import org.openstreetmap.josm.data.osm.UserInfo;
018import org.openstreetmap.josm.gui.ExceptionDialogUtil;
019import org.openstreetmap.josm.gui.MainApplication;
020import org.openstreetmap.josm.gui.PleaseWaitRunnable;
021import org.openstreetmap.josm.gui.util.GuiHelper;
022import org.openstreetmap.josm.io.ChangesetQuery;
023import org.openstreetmap.josm.io.OsmServerChangesetReader;
024import org.openstreetmap.josm.io.OsmServerUserInfoReader;
025import org.openstreetmap.josm.io.OsmTransferException;
026import org.openstreetmap.josm.tools.Logging;
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 (UserIdentityManager.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            Logging.warn(msg);
068            JOptionPane.showMessageDialog(GuiHelper.getFrameForComponent(parent),
069                    "<html>" + msg + "</html>", tr("Missing user identity"), JOptionPane.ERROR_MESSAGE);
070            return;
071        }
072        if (canceled) return;
073        if (lastException != null) {
074            ExceptionDialogUtil.explainException(lastException);
075            return;
076        }
077        if (changesets.isEmpty()) {
078            JOptionPane.showMessageDialog(
079                    MainApplication.getMainFrame(),
080                    tr("There are no open changesets"),
081                    tr("No open changesets"),
082                    JOptionPane.INFORMATION_MESSAGE
083            );
084            return;
085        }
086        SwingUtilities.invokeLater(() -> ChangesetCache.getInstance().update(changesets));
087    }
088
089    /**
090     * Refreshes the user info from the server. This is necessary if we don't know the users id yet.
091     */
092    protected void refreshUserIdentity() {
093        UserIdentityManager im = UserIdentityManager.getInstance();
094        try {
095            OsmServerUserInfoReader infoReader = new OsmServerUserInfoReader();
096            UserInfo info = infoReader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1, false));
097            im.setFullyIdentified(info.getDisplayName(), info);
098        } catch (OsmTransferException e) {
099            // retrieving the user info can fail if the current user is not authorised to
100            // retrieve it, i.e. if he is working with an OAuth Access Token which doesn't
101            // have the respective privileges or if he didn't or he can't authenticate with
102            // a username/password-pair.
103            //
104            // Downgrade your knowledge about its identity if we've assumed that he was fully
105            // identified. Otherwise, if he is anonymous or partially identified, keep our level
106            // of knowledge.
107            //
108            if (im.isFullyIdentified()) {
109                im.setPartiallyIdentified(im.getUserName());
110            }
111            Logging.log(Logging.LEVEL_WARN,
112                    tr("Failed to retrieve user infos for the current JOSM user. Exception was: {0}", e.toString()), e);
113        }
114    }
115
116    @Override
117    protected void realRun() throws SAXException, IOException, OsmTransferException {
118        try {
119            UserIdentityManager im = UserIdentityManager.getInstance();
120            if (im.isAnonymous()) {
121                refreshUserIdentity();
122            } else if (im.isFullyIdentified()) {
123                // do nothing
124            } else if (im.isPartiallyIdentified()) {
125                refreshUserIdentity();
126            }
127            if (canceled) return;
128            synchronized (this) {
129                reader = new OsmServerChangesetReader();
130            }
131            ChangesetQuery query = new ChangesetQuery().beingOpen(true);
132            if (im.isAnonymous())
133                // we still don't know anything about the current user. Can't retrieve
134                // its changesets
135                return;
136            else if (im.isFullyIdentified()) {
137                query = query.forUser(im.getUserId());
138            } else {
139                // we only know the users name, not its id. Nevermind, try to read
140                // its open changesets anyway.
141                //
142                query = query.forUser(im.getUserName());
143            }
144            changesets = reader.queryChangesets(
145                    query,
146                    getProgressMonitor().createSubTaskMonitor(1, false /* not internal */)
147            );
148        } catch (OsmTransferException e) {
149            if (canceled)
150                return;
151            lastException = e;
152        }
153    }
154
155    /**
156     * Determines if this task has been cancelled.
157     * @return {@code true} if this task has been cancelled
158     */
159    public boolean isCanceled() {
160        return canceled;
161    }
162
163    /**
164     * Returns the changesets.
165     * @return the changesets, or {@code null}
166     * @since 11110
167     */
168    public final List<Changeset> getChangesets() {
169        return changesets != null ? Collections.unmodifiableList(changesets) : null;
170    }
171}