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 * 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 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 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 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 @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 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<>(); 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 } catch(OsmTransferException e) { 190 if (canceled) 191 return; 192 this.lastException = e; 193 } 194 } 195 196 /* ------------------------------------------------------------------------------- */ 197 /* interface ChangesetDownloadTask */ 198 /* ------------------------------------------------------------------------------- */ 199 @Override 200 public Set<Changeset> getDownloadedChangesets() { 201 return downloadedChangesets; 202 } 203 204 @Override 205 public boolean isCanceled() { 206 return canceled; 207 } 208 209 @Override 210 public boolean isFailed() { 211 return lastException != null; 212 } 213}