001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.changeset.query; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.io.IOException; 008import java.lang.reflect.InvocationTargetException; 009import java.util.HashSet; 010import java.util.Set; 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.JosmUserIdentityManager; 020import org.openstreetmap.josm.gui.PleaseWaitRunnable; 021import org.openstreetmap.josm.gui.dialogs.changeset.ChangesetDownloadTask; 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.OsmTransferCanceledException; 027import org.openstreetmap.josm.io.OsmTransferException; 028import org.openstreetmap.josm.tools.BugReportExceptionHandler; 029import org.openstreetmap.josm.tools.CheckParameterUtil; 030import org.openstreetmap.josm.tools.ExceptionUtil; 031import org.xml.sax.SAXException; 032 033/** 034 * Asynchronous task to send a changeset query to the OSM API. 035 * @since 2689 036 */ 037public 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 if query is null. 057 */ 058 public ChangesetQueryTask(ChangesetQuery query) { 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 org.openstreetmap.josm.gui.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 if query is null. 071 * @throws IllegalArgumentException if parent is null 072 */ 073 public ChangesetQueryTask(Component parent, ChangesetQuery query) { 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 @Override 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 Main.warn("InterruptedException in "+getClass().getSimpleName()+" while updating changeset cache"); 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 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 first. 169 // 170 if (im.isPartiallyIdentified()) { 171 fullyIdentifyCurrentUser(); 172 } 173 query = query.forUser(JosmUserIdentityManager.getInstance().getUserId()); 174 } 175 if (canceled) return; 176 getProgressMonitor().indeterminateSubTask(tr("Query and download changesets ...")); 177 synchronized (this) { 178 changesetReader = new OsmServerChangesetReader(); 179 } 180 downloadedChangesets = new HashSet<>(); 181 downloadedChangesets.addAll(changesetReader.queryChangesets(query, getProgressMonitor().createSubTaskMonitor(0, false))); 182 synchronized (this) { 183 changesetReader = null; 184 } 185 } catch (OsmTransferCanceledException e) { 186 // thrown if user cancel the authentication dialog 187 canceled = true; 188 } catch (OsmTransferException e) { 189 if (canceled) 190 return; 191 this.lastException = e; 192 } 193 } 194 195 /* ------------------------------------------------------------------------------- */ 196 /* interface ChangesetDownloadTask */ 197 /* ------------------------------------------------------------------------------- */ 198 @Override 199 public Set<Changeset> getDownloadedChangesets() { 200 return downloadedChangesets; 201 } 202 203 @Override 204 public boolean isCanceled() { 205 return canceled; 206 } 207 208 @Override 209 public boolean isFailed() { 210 return lastException != null; 211 } 212}