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