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