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() || notesData == null || notesData.isEmpty()) {
109                return;
110            }
111            if (Main.isDebugEnabled()) {
112                Main.debug("Notes downloaded: " + notesData.size());
113            }
114
115            List<NoteLayer> noteLayers = Main.getLayerManager().getLayersOfType(NoteLayer.class);
116            if (!noteLayers.isEmpty()) {
117                noteLayers.get(0).getNoteData().addNotes(notesData);
118            } else {
119                Main.main.addLayer(new NoteLayer(notesData, tr("Notes")));
120            }
121        }
122
123        @Override
124        protected void cancel() {
125            setCanceled(true);
126            if (reader != null) {
127                reader.cancel();
128            }
129        }
130
131        @Override
132        public abstract void realRun() throws IOException, SAXException, OsmTransferException;
133    }
134
135    class DownloadBoundingBoxTask extends DownloadTask {
136
137        DownloadBoundingBoxTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
138            super(reader, progressMonitor);
139        }
140
141        @Override
142        public void realRun() throws IOException, SAXException, OsmTransferException {
143            if (isCanceled()) {
144                return;
145            }
146            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
147            try {
148                notesData = reader.parseNotes(DOWNLOAD_LIMIT.get(), DAYS_CLOSED.get(), subMonitor);
149            } catch (BoundingBoxDownloader.MoreNotesException e) {
150                notesData = e.notes;
151                JOptionPane.showMessageDialog(Main.parent, "<html>"
152                                + trn("{0} note has been downloaded.", "{0} notes have been downloaded.", e.limit, e.limit)
153                                + "<br>"
154                                + tr("Since the download limit was {0}, there might be more notes to download.", e.limit)
155                                + "<br>"
156                                + tr("Request a smaller area to make sure that all notes are being downloaded.")
157                                + "</html>",
158                        tr("More notes to download"), JOptionPane.INFORMATION_MESSAGE);
159            } catch (OsmTransferException e) {
160                if (isCanceled())
161                    return;
162                rememberException(e);
163            }
164        }
165    }
166
167    class DownloadRawUrlTask extends DownloadTask {
168
169        DownloadRawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
170            super(reader, progressMonitor);
171        }
172
173        @Override
174        public void realRun() throws IOException, SAXException, OsmTransferException {
175            if (isCanceled()) {
176                return;
177            }
178            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
179            try {
180                notesData = reader.parseRawNotes(subMonitor);
181            } catch (OsmTransferException e) {
182                if (isCanceled())
183                    return;
184                rememberException(e);
185            }
186        }
187    }
188
189    class DownloadBzip2RawUrlTask extends DownloadTask {
190
191        DownloadBzip2RawUrlTask(OsmServerReader reader, ProgressMonitor progressMonitor) {
192            super(reader, progressMonitor);
193        }
194
195        @Override
196        public void realRun() throws IOException, SAXException, OsmTransferException {
197            if (isCanceled()) {
198                return;
199            }
200            ProgressMonitor subMonitor = progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false);
201            try {
202                notesData = reader.parseRawNotesBzip2(subMonitor);
203            } catch (OsmTransferException e) {
204                if (isCanceled())
205                    return;
206                rememberException(e);
207            }
208        }
209    }
210}