001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions.downloadtasks; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.io.IOException; 008import java.net.URL; 009import java.util.List; 010import java.util.concurrent.Future; 011 012import javax.swing.JOptionPane; 013 014import org.openstreetmap.josm.data.Bounds; 015import org.openstreetmap.josm.data.ProjectionBounds; 016import org.openstreetmap.josm.data.ViewportData; 017import org.openstreetmap.josm.data.notes.Note; 018import org.openstreetmap.josm.data.osm.NoteData; 019import org.openstreetmap.josm.data.preferences.IntegerProperty; 020import org.openstreetmap.josm.gui.MainApplication; 021import org.openstreetmap.josm.gui.MapFrame; 022import org.openstreetmap.josm.gui.PleaseWaitRunnable; 023import org.openstreetmap.josm.gui.layer.NoteLayer; 024import org.openstreetmap.josm.gui.progress.ProgressMonitor; 025import org.openstreetmap.josm.io.BoundingBoxDownloader; 026import org.openstreetmap.josm.io.BoundingBoxDownloader.MoreNotesException; 027import org.openstreetmap.josm.io.Compression; 028import org.openstreetmap.josm.io.OsmApi; 029import org.openstreetmap.josm.io.OsmServerLocationReader; 030import org.openstreetmap.josm.io.OsmServerReader; 031import org.openstreetmap.josm.io.OsmTransferException; 032import org.openstreetmap.josm.io.UrlPatterns.NoteUrlPattern; 033import org.openstreetmap.josm.tools.Logging; 034import org.xml.sax.SAXException; 035 036/** 037 * General task for downloading OSM notes. 038 * <p> 039 * It handles two URL patterns: API call and dump file export. 040 * @since 7531 041 */ 042public class DownloadNotesTask extends AbstractDownloadTask<NoteData> { 043 044 /** Property defining the number of notes to be downloaded */ 045 public static final IntegerProperty DOWNLOAD_LIMIT = new IntegerProperty("osm.notes.downloadLimit", 1000); 046 /** Property defining number of days a bug needs to be closed to no longer be downloaded */ 047 public static final IntegerProperty DAYS_CLOSED = new IntegerProperty("osm.notes.daysClosed", 7); 048 049 private static final String PATTERN_COMPRESS = "https?://.*/(.*\\.osn.(gz|xz|bz2?|zip))"; 050 private static final String NO_NOTES_FOUND = tr("No notes found in this area."); 051 static { 052 PostDownloadHandler.addNoDataErrorMessage(NO_NOTES_FOUND); 053 } 054 055 private DownloadTask downloadTask; 056 private NoteLayer noteLayer; 057 058 /** This allows subclasses to ignore this warning */ 059 protected boolean warnAboutEmptyArea = true; 060 061 /** 062 * Download a specific note by its id. 063 * @param id Note identifier 064 * @param progressMonitor progress monitor 065 * @return the future representing the asynchronous task 066 */ 067 public Future<?> download(long id, ProgressMonitor progressMonitor) { 068 final String url = OsmApi.getOsmApi().getBaseUrl() + "notes/" + id; 069 downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor); 070 return MainApplication.worker.submit(downloadTask); 071 } 072 073 @Override 074 public Future<?> download(DownloadParams settings, Bounds downloadArea, ProgressMonitor progressMonitor) { 075 downloadTask = new DownloadBoundingBoxTask(new BoundingBoxDownloader(downloadArea), progressMonitor); 076 return MainApplication.worker.submit(downloadTask); 077 } 078 079 @Override 080 public Future<?> loadUrl(DownloadParams settings, String url, ProgressMonitor progressMonitor) { 081 if (url.matches(PATTERN_COMPRESS)) { 082 downloadTask = new DownloadCompressedRawUrlTask(new OsmServerLocationReader(url), progressMonitor, Compression.byExtension(url)); 083 } else { 084 downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor); 085 } 086 return MainApplication.worker.submit(downloadTask); 087 } 088 089 @Override 090 public void cancel() { 091 if (downloadTask != null) { 092 downloadTask.cancel(); 093 } 094 } 095 096 @Override 097 public String getConfirmationMessage(URL url) { 098 return null; 099 } 100 101 @Override 102 public String getTitle() { 103 return tr("Download OSM Notes"); 104 } 105 106 @Override 107 public String[] getPatterns() { 108 return patterns(NoteUrlPattern.class); 109 } 110 111 @Override 112 public boolean isSafeForRemotecontrolRequests() { 113 return true; 114 } 115 116 @Override 117 public ProjectionBounds getDownloadProjectionBounds() { 118 return noteLayer != null ? noteLayer.getViewProjectionBounds() : null; 119 } 120 121 abstract class DownloadTask extends PleaseWaitRunnable { 122 protected OsmServerReader reader; 123 protected List<Note> notesData; 124 125 DownloadTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 126 super(tr("Downloading notes"), progressMonitor, false); 127 this.reader = reader; 128 } 129 130 @Override 131 protected void finish() { 132 rememberDownloadedData(new NoteData(notesData)); 133 if (isCanceled() || isFailed() || notesData == null) { 134 return; 135 } 136 if (notesData.isEmpty()) { 137 if (warnAboutEmptyArea) { 138 rememberErrorMessage(NO_NOTES_FOUND); 139 } 140 return; 141 } 142 if (Logging.isDebugEnabled()) { 143 Logging.debug("Notes downloaded: {0}", notesData.size()); 144 } 145 146 noteLayer = new NoteLayer(notesData, tr("Notes")); 147 NoteLayer l = MainApplication.getLayerManager().getNoteLayer(); 148 if (l != null) { 149 l.mergeFrom(noteLayer); 150 MapFrame map = MainApplication.getMap(); 151 if (map != null && zoomAfterDownload) { 152 map.mapView.scheduleZoomTo(new ViewportData(noteLayer.getViewProjectionBounds())); 153 } 154 } else { 155 MainApplication.getLayerManager().addLayer(noteLayer, zoomAfterDownload); 156 } 157 } 158 159 @Override 160 protected void cancel() { 161 setCanceled(true); 162 if (reader != null) { 163 reader.cancel(); 164 } 165 } 166 167 @Override 168 public abstract void realRun() throws IOException, SAXException, OsmTransferException; 169 } 170 171 class DownloadBoundingBoxTask extends DownloadTask { 172 173 DownloadBoundingBoxTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 174 super(reader, progressMonitor); 175 } 176 177 @Override 178 public void realRun() throws IOException, SAXException, OsmTransferException { 179 if (isCanceled()) { 180 return; 181 } 182 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 183 try { 184 notesData = reader.parseNotes(DOWNLOAD_LIMIT.get(), DAYS_CLOSED.get(), subMonitor); 185 } catch (MoreNotesException e) { 186 Logging.debug(e); 187 notesData = e.notes; 188 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), "<html>" 189 + trn("{0} note has been downloaded.", "{0} notes have been downloaded.", e.limit, e.limit) 190 + "<br>" 191 + tr("Since the download limit was {0}, there might be more notes to download.", e.limit) 192 + "<br>" 193 + tr("Request a smaller area to make sure that all notes are being downloaded.") 194 + "</html>", 195 tr("More notes to download"), JOptionPane.INFORMATION_MESSAGE); 196 } catch (OsmTransferException e) { 197 if (isCanceled()) 198 return; 199 rememberException(e); 200 } 201 } 202 } 203 204 class DownloadRawUrlTask extends DownloadTask { 205 206 DownloadRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 207 super(reader, progressMonitor); 208 } 209 210 @Override 211 public void realRun() throws IOException, SAXException, OsmTransferException { 212 if (isCanceled()) { 213 return; 214 } 215 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 216 try { 217 notesData = reader.parseRawNotes(subMonitor); 218 } catch (OsmTransferException e) { 219 if (isCanceled()) 220 return; 221 rememberException(e); 222 } 223 } 224 } 225 226 class DownloadCompressedRawUrlTask extends DownloadTask { 227 228 private final Compression compression; 229 230 DownloadCompressedRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor, Compression compression) { 231 super(reader, progressMonitor); 232 this.compression = compression; 233 } 234 235 @Override 236 public void realRun() throws IOException, SAXException, OsmTransferException { 237 if (isCanceled()) { 238 return; 239 } 240 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 241 try { 242 notesData = reader.parseRawNotes(subMonitor, compression); 243 } catch (OsmTransferException e) { 244 if (isCanceled()) 245 return; 246 rememberException(e); 247 } 248 } 249 } 250}