001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.dialogs.changeset.query;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.Component;
007    import java.io.IOException;
008    import java.lang.reflect.InvocationTargetException;
009    import java.util.HashSet;
010    import java.util.Set;
011    
012    import javax.swing.JOptionPane;
013    import javax.swing.SwingUtilities;
014    
015    import org.openstreetmap.josm.Main;
016    import org.openstreetmap.josm.data.osm.Changeset;
017    import org.openstreetmap.josm.data.osm.ChangesetCache;
018    import org.openstreetmap.josm.data.osm.UserInfo;
019    import org.openstreetmap.josm.gui.JosmUserIdentityManager;
020    import org.openstreetmap.josm.gui.PleaseWaitRunnable;
021    import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetDownloadTask;
022    import org.openstreetmap.josm.gui.util.GuiHelper;
023    import org.openstreetmap.josm.io.ChangesetQuery;
024    import org.openstreetmap.josm.io.OsmServerChangesetReader;
025    import org.openstreetmap.josm.io.OsmServerUserInfoReader;
026    import org.openstreetmap.josm.io.OsmTransferCanceledException;
027    import org.openstreetmap.josm.io.OsmTransferException;
028    import org.openstreetmap.josm.tools.BugReportExceptionHandler;
029    import org.openstreetmap.josm.tools.CheckParameterUtil;
030    import org.openstreetmap.josm.tools.ExceptionUtil;
031    import org.xml.sax.SAXException;
032    
033    /**
034     * Asynchronous task to send a changeset query to the OSM API.
035     *
036     */
037    public class ChangesetQueryTask extends PleaseWaitRunnable implements ChangesetDownloadTask{
038    
039        /** the changeset query */
040        private ChangesetQuery query;
041        /** true if the task was canceled */
042        private boolean canceled;
043        /** the set of downloaded changesets */
044        private Set<Changeset> downloadedChangesets;
045        /** the last exception remembered, if any */
046        private Exception lastException;
047        /** the reader object used to read information about the current user from the API */
048        private OsmServerUserInfoReader userInfoReader;
049        /** the reader object used to submit the changeset query to the API */
050        private OsmServerChangesetReader changesetReader;
051    
052        /**
053         * Creates the task.
054         *
055         * @param query the query to submit to the OSM server. Must not be null.
056         * @throws IllegalArgumentException thrown if query is null.
057         */
058        public ChangesetQueryTask(ChangesetQuery query) throws IllegalArgumentException {
059            super(tr("Querying and downloading changesets",false /* don't ignore exceptions */));
060            CheckParameterUtil.ensureParameterNotNull(query, "query");
061            this.query = query;
062        }
063    
064        /**
065         * Creates the task.
066         *
067         * @param parent the parent component relative to which the {@link PleaseWaitDialog} is displayed.
068         * Must not be null.
069         * @param query the query to submit to the OSM server. Must not be null.
070         * @throws IllegalArgumentException thrown if query is null.
071         * @throws IllegalArgumentException thrown if parent is null
072         */
073        public ChangesetQueryTask(Component parent, ChangesetQuery query) throws IllegalArgumentException {
074            super(parent, tr("Querying and downloading changesets"), false /* don't ignore exceptions */);
075            CheckParameterUtil.ensureParameterNotNull(query, "query");
076            this.query = query;
077        }
078    
079        @Override
080        protected void cancel() {
081            canceled = true;
082            synchronized(this) {
083                if (userInfoReader != null) {
084                    userInfoReader.cancel();
085                }
086            }
087            synchronized(this) {
088                if (changesetReader != null) {
089                    changesetReader.cancel();
090                }
091            }
092        }
093    
094        @Override
095        protected void finish() {
096            if (canceled) return;
097            if (lastException != null) {
098                GuiHelper.runInEDTAndWait(new Runnable() {
099                    private final Component parent = progressMonitor != null ? progressMonitor.getWindowParent() : null;
100                    @Override
101                    public void run() {
102                        JOptionPane.showMessageDialog(
103                                parent != null ? parent : Main.parent,
104                                ExceptionUtil.explainException(lastException),
105                                tr("Errors during download"),
106                                JOptionPane.ERROR_MESSAGE);
107                    }
108                });
109                return;
110            }
111    
112            // update the global changeset cache with the downloaded changesets;
113            // this will trigger change events which views are listening to. They
114            // will update their views accordingly.
115            //
116            // Run on the EDT because UI updates are triggered.
117            //
118            Runnable r = new Runnable() {
119                public void run() {
120                    ChangesetCache.getInstance().update(downloadedChangesets);
121                }
122            };
123            if (SwingUtilities.isEventDispatchThread()) {
124                r.run();
125            } else {
126                try {
127                    SwingUtilities.invokeAndWait(r);
128                } catch(InterruptedException e) {
129                    e.printStackTrace();
130                } catch(InvocationTargetException e) {
131                    Throwable t = e.getTargetException();
132                    if (t instanceof RuntimeException) {
133                        BugReportExceptionHandler.handleException(t);
134                    } else if (t instanceof Exception){
135                        ExceptionUtil.explainException(e);
136                    } else {
137                        BugReportExceptionHandler.handleException(t);
138                    }
139                }
140            }
141        }
142    
143        /**
144         * Tries to fully identify the current JOSM user
145         *
146         * @throws OsmTransferException thrown if something went wrong
147         */
148        protected void fullyIdentifyCurrentUser() throws OsmTransferException {
149            getProgressMonitor().indeterminateSubTask(tr("Determine user id for current user..."));
150    
151            synchronized(this) {
152                userInfoReader = new OsmServerUserInfoReader();
153            }
154            UserInfo info = userInfoReader.fetchUserInfo(getProgressMonitor().createSubTaskMonitor(1,false));
155            synchronized(this) {
156                userInfoReader = null;
157            }
158            JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
159            im.setFullyIdentified(im.getUserName(), info);
160        }
161    
162        @Override
163        protected void realRun() throws SAXException, IOException, OsmTransferException {
164            try {
165                JosmUserIdentityManager im = JosmUserIdentityManager.getInstance();
166                if (query.isRestrictedToPartiallyIdentifiedUser() && im.isCurrentUser(query.getUserName())) {
167                    // if we query changesets for the current user, make sure we query against
168                    // its user id, not its user name. If necessary, determine the user id
169                    // first.
170                    //
171                    if (im.isPartiallyIdentified() ) {
172                        fullyIdentifyCurrentUser();
173                    }
174                    query = query.forUser(JosmUserIdentityManager.getInstance().getUserId());
175                }
176                if (canceled) return;
177                getProgressMonitor().indeterminateSubTask(tr("Query and download changesets ..."));
178                synchronized(this) {
179                    changesetReader= new OsmServerChangesetReader();
180                }
181                downloadedChangesets = new HashSet<Changeset>();
182                downloadedChangesets.addAll(changesetReader.queryChangesets(query, getProgressMonitor().createSubTaskMonitor(0, false)));
183                synchronized (this) {
184                    changesetReader = null;
185                }
186            } catch(OsmTransferCanceledException e) {
187                // thrown if user cancel the authentication dialog
188                canceled = true;
189                return;
190            }  catch(OsmTransferException e) {
191                if (canceled)
192                    return;
193                this.lastException = e;
194            }
195        }
196    
197        /* ------------------------------------------------------------------------------- */
198        /* interface ChangesetDownloadTask                                                 */
199        /* ------------------------------------------------------------------------------- */
200        public Set<Changeset> getDownloadedChangesets() {
201            return downloadedChangesets;
202        }
203    
204        public boolean isCanceled() {
205            return canceled;
206        }
207    
208        public boolean isFailed() {
209            return lastException != null;
210        }
211    }