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