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 052 private DownloadTask downloadTask; 053 private NoteLayer noteLayer; 054 055 /** 056 * Download a specific note by its id. 057 * @param id Note identifier 058 * @param progressMonitor progress monitor 059 * @return the future representing the asynchronous task 060 */ 061 public Future<?> download(long id, ProgressMonitor progressMonitor) { 062 final String url = OsmApi.getOsmApi().getBaseUrl() + "notes/" + id; 063 downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor); 064 return MainApplication.worker.submit(downloadTask); 065 } 066 067 @Override 068 public Future<?> download(DownloadParams settings, Bounds downloadArea, ProgressMonitor progressMonitor) { 069 downloadTask = new DownloadBoundingBoxTask(new BoundingBoxDownloader(downloadArea), progressMonitor); 070 return MainApplication.worker.submit(downloadTask); 071 } 072 073 @Override 074 public Future<?> loadUrl(DownloadParams settings, String url, ProgressMonitor progressMonitor) { 075 if (url.matches(PATTERN_COMPRESS)) { 076 downloadTask = new DownloadCompressedRawUrlTask(new OsmServerLocationReader(url), progressMonitor, Compression.byExtension(url)); 077 } else { 078 downloadTask = new DownloadRawUrlTask(new OsmServerLocationReader(url), progressMonitor); 079 } 080 return MainApplication.worker.submit(downloadTask); 081 } 082 083 @Override 084 public void cancel() { 085 if (downloadTask != null) { 086 downloadTask.cancel(); 087 } 088 } 089 090 @Override 091 public String getConfirmationMessage(URL url) { 092 return null; 093 } 094 095 @Override 096 public String getTitle() { 097 return tr("Download OSM Notes"); 098 } 099 100 @Override 101 public String[] getPatterns() { 102 return Arrays.stream(NoteUrlPattern.values()).map(NoteUrlPattern::pattern).toArray(String[]::new); 103 } 104 105 @Override 106 public boolean isSafeForRemotecontrolRequests() { 107 return true; 108 } 109 110 @Override 111 public ProjectionBounds getDownloadProjectionBounds() { 112 return noteLayer != null ? noteLayer.getViewProjectionBounds() : null; 113 } 114 115 abstract class DownloadTask extends PleaseWaitRunnable { 116 protected OsmServerReader reader; 117 protected List<Note> notesData; 118 119 DownloadTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 120 super(tr("Downloading notes"), progressMonitor, false); 121 this.reader = reader; 122 } 123 124 @Override 125 protected void finish() { 126 rememberDownloadedData(new NoteData(notesData)); 127 if (isCanceled() || isFailed() || notesData == null || notesData.isEmpty()) { 128 return; 129 } 130 if (Logging.isDebugEnabled()) { 131 Logging.debug("Notes downloaded: {0}", notesData.size()); 132 } 133 134 noteLayer = new NoteLayer(notesData, tr("Notes")); 135 NoteLayer l = MainApplication.getLayerManager().getNoteLayer(); 136 if (l != null) { 137 l.mergeFrom(noteLayer); 138 MapFrame map = MainApplication.getMap(); 139 if (map != null && zoomAfterDownload) { 140 map.mapView.scheduleZoomTo(new ViewportData(noteLayer.getViewProjectionBounds())); 141 } 142 } else { 143 MainApplication.getLayerManager().addLayer(noteLayer, zoomAfterDownload); 144 } 145 } 146 147 @Override 148 protected void cancel() { 149 setCanceled(true); 150 if (reader != null) { 151 reader.cancel(); 152 } 153 } 154 155 @Override 156 public abstract void realRun() throws IOException, SAXException, OsmTransferException; 157 } 158 159 class DownloadBoundingBoxTask extends DownloadTask { 160 161 DownloadBoundingBoxTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 162 super(reader, progressMonitor); 163 } 164 165 @Override 166 public void realRun() throws IOException, SAXException, OsmTransferException { 167 if (isCanceled()) { 168 return; 169 } 170 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 171 try { 172 notesData = reader.parseNotes(DOWNLOAD_LIMIT.get(), DAYS_CLOSED.get(), subMonitor); 173 } catch (MoreNotesException e) { 174 Logging.debug(e); 175 notesData = e.notes; 176 JOptionPane.showMessageDialog(MainApplication.getMainFrame(), "<html>" 177 + trn("{0} note has been downloaded.", "{0} notes have been downloaded.", e.limit, e.limit) 178 + "<br>" 179 + tr("Since the download limit was {0}, there might be more notes to download.", e.limit) 180 + "<br>" 181 + tr("Request a smaller area to make sure that all notes are being downloaded.") 182 + "</html>", 183 tr("More notes to download"), JOptionPane.INFORMATION_MESSAGE); 184 } catch (OsmTransferException e) { 185 if (isCanceled()) 186 return; 187 rememberException(e); 188 } 189 } 190 } 191 192 class DownloadRawUrlTask extends DownloadTask { 193 194 DownloadRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) { 195 super(reader, progressMonitor); 196 } 197 198 @Override 199 public void realRun() throws IOException, SAXException, OsmTransferException { 200 if (isCanceled()) { 201 return; 202 } 203 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 204 try { 205 notesData = reader.parseRawNotes(subMonitor); 206 } catch (OsmTransferException e) { 207 if (isCanceled()) 208 return; 209 rememberException(e); 210 } 211 } 212 } 213 214 class DownloadCompressedRawUrlTask extends DownloadTask { 215 216 private final Compression compression; 217 218 DownloadCompressedRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor, Compression compression) { 219 super(reader, progressMonitor); 220 this.compression = compression; 221 } 222 223 @Override 224 public void realRun() throws IOException, SAXException, OsmTransferException { 225 if (isCanceled()) { 226 return; 227 } 228 ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false); 229 try { 230 notesData = reader.parseRawNotes(subMonitor, compression); 231 } catch (OsmTransferException e) { 232 if (isCanceled()) 233 return; 234 rememberException(e); 235 } 236 } 237 } 238}