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