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