Ticket #18340: 18340.patch

File 18340.patch, 21.5 KB (added by taylor.smock, 6 years ago)
  • src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java

     
    77import java.awt.Dimension;
    88import java.awt.Font;
    99import java.awt.GridBagLayout;
     10import java.lang.reflect.InvocationTargetException;
    1011import java.util.ArrayList;
     12import java.util.Collections;
    1113import java.util.List;
    1214import java.util.concurrent.ExecutionException;
    1315import java.util.concurrent.Future;
     
    2325import org.openstreetmap.josm.actions.downloadtasks.DownloadNotesTask;
    2426import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
    2527import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
     28import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
    2629import org.openstreetmap.josm.actions.downloadtasks.PostDownloadHandler;
    2730import org.openstreetmap.josm.data.Bounds;
    2831import org.openstreetmap.josm.data.ProjectionBounds;
    2932import org.openstreetmap.josm.data.ViewportData;
     33import org.openstreetmap.josm.data.gpx.GpxData;
     34import org.openstreetmap.josm.data.osm.DataSet;
     35import org.openstreetmap.josm.data.osm.NoteData;
    3036import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3137import org.openstreetmap.josm.gui.MainApplication;
    3238import org.openstreetmap.josm.gui.MapFrame;
     
    4854     */
    4955    public static final String SIMPLE_NAME = "osmdownloadpanel";
    5056
     57    /** The possible methods to get data */
     58    static final List<DataDownloadType> DOWNLOAD_POSSIBILITIES = new ArrayList<>();
     59    static {
     60        // Order is important (determines button order, and what gets zoomed to)
     61        DOWNLOAD_POSSIBILITIES.add(new OsmDataDownloadType());
     62        DOWNLOAD_POSSIBILITIES.add(new GpsDataDownloadType());
     63        DOWNLOAD_POSSIBILITIES.add(new NotesDataDownloadType());
     64    }
     65
    5166    @Override
    5267    public AbstractDownloadSourcePanel<OSMDownloadData> createPanel(DownloadDialog dialog) {
    5368        return new OSMDownloadSourcePanel(this, dialog);
     
    5974                .orElseThrow(() -> new IllegalArgumentException("OSM downloads requires bounds"));
    6075        boolean zoom = settings.zoomToData();
    6176        boolean newLayer = settings.asNewLayer();
    62         List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
    63 
    64         if (data.isDownloadOSMData()) {
    65             DownloadOsmTask task = new DownloadOsmTask();
    66             task.setZoomAfterDownload(zoom && !data.isDownloadGPX() && !data.isDownloadNotes());
    67             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    68             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    69             if (zoom) {
    70                 tasks.add(new Pair<>(task, future));
     77        final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
     78        DataDownloadType zoomTask = zoom ? data.getDownloadPossibilities().stream().findFirst().orElse(null) : null;
     79        data.getDownloadPossibilities().parallelStream().filter(DataDownloadType::isEnabled).forEach(type -> {
     80            try {
     81                AbstractDownloadTask<?> task = type.getDownloadClass().getDeclaredConstructor().newInstance();
     82                task.setZoomAfterDownload(type.equals(zoomTask));
     83                Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
     84                MainApplication.worker.submit(new PostDownloadHandler(task, future));
     85                if (zoom) {
     86                    tasks.add(new Pair<AbstractDownloadTask<?>, Future<?>>(task, future));
     87                }
     88            } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
     89                    | InvocationTargetException | NoSuchMethodException | SecurityException e) {
     90                Logging.error(e);
    7191            }
    72         }
     92        });
    7393
    74         if (data.isDownloadGPX()) {
    75             DownloadGpsTask task = new DownloadGpsTask();
    76             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadNotes());
    77             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    78             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    79             if (zoom) {
    80                 tasks.add(new Pair<>(task, future));
    81             }
    82         }
    83 
    84         if (data.isDownloadNotes()) {
    85             DownloadNotesTask task = new DownloadNotesTask();
    86             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadGPX());
    87             Future<?> future = task.download(new DownloadParams(), bbox, null);
    88             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    89             if (zoom) {
    90                 tasks.add(new Pair<>(task, future));
    91             }
    92         }
    93 
    9494        if (zoom && tasks.size() > 1) {
    9595            MainApplication.worker.submit(() -> {
    9696                ProjectionBounds bounds = null;
     
    129129    }
    130130
    131131    /**
     132     * @return The possible downloads that JOSM can make in the default Download
     133     *         screen
     134     * @since xxx
     135     */
     136    public List<DataDownloadType> getDownloadTypes() {
     137        return Collections.unmodifiableList(DOWNLOAD_POSSIBILITIES);
     138    }
     139
     140    /**
     141     * @param type The DataDownloadType object to remove
     142     * @return true if the list was modified
     143     * @since xxx
     144     */
     145    public boolean removeDownloadType(DataDownloadType type) {
     146        boolean modified = false;
     147        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     148                && !(type instanceof NotesDataDownloadType)) {
     149            modified = DOWNLOAD_POSSIBILITIES.remove(type);
     150        }
     151        return modified;
     152    }
     153
     154    /**
     155     * Add a download type to the default JOSM download window
     156     *
     157     * @param type The initialized type to download
     158     * @return true if the list was modified
     159     * @since xxx
     160     */
     161    public boolean addDownloadType(DataDownloadType type) {
     162        boolean modified = false;
     163        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     164                && !(type instanceof NotesDataDownloadType)
     165                || DOWNLOAD_POSSIBILITIES.parallelStream()
     166                        .noneMatch(possibility -> type.getClass().isInstance(possibility))) {
     167            modified = DOWNLOAD_POSSIBILITIES.add(type);
     168        }
     169        return modified;
     170    }
     171
     172    /**
    132173     * The GUI representation of the OSM download source.
    133174     * @since 12652
    134175     */
    135176    public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<OSMDownloadData> {
    136 
    137         private final JCheckBox cbDownloadOsmData;
    138         private final JCheckBox cbDownloadGpxData;
    139         private final JCheckBox cbDownloadNotes;
    140177        private final JLabel sizeCheck = new JLabel();
    141178
    142         private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm.data", true);
    143         private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.osm.gps", false);
    144         private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.osm.notes", false);
    145 
    146179        /**
    147180         * Creates a new {@link OSMDownloadSourcePanel}.
    148181         * @param dialog the parent download dialog, as {@code DownloadDialog.getInstance()} might not be initialized yet
     
    159192
    160193            // adding the download tasks
    161194            add(new JLabel(tr("Data Sources and Types:")), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER));
    162             cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
    163             cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
    164             cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
    165 
    166             cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
    167             cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
    168             cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
    169 
    170             cbDownloadNotes = new JCheckBox(tr("Notes"));
    171             cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
    172             cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
    173 
    174195            Font labelFont = sizeCheck.getFont();
    175196            sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
    176197
    177             add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
    178             add(cbDownloadGpxData, GBC.std().insets(1, 5, 1, 5));
    179             add(cbDownloadNotes, GBC.eol().insets(1, 5, 1, 5));
     198            DOWNLOAD_POSSIBILITIES
     199                    .forEach(obj -> add(obj.getCheckBox(checkboxChangeListener), GBC.std().insets(1, 5, 1, 5)));
    180200            add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
    181201
    182202            setMinimumSize(new Dimension(450, 115));
     
    184204
    185205        @Override
    186206        public OSMDownloadData getData() {
    187             return new OSMDownloadData(
    188                     isDownloadOsmData(),
    189                     isDownloadNotes(),
    190                     isDownloadGpxData());
     207            return new OSMDownloadData(DOWNLOAD_POSSIBILITIES);
    191208        }
    192209
    193210        @Override
    194211        public void rememberSettings() {
    195             DOWNLOAD_OSM.put(isDownloadOsmData());
    196             DOWNLOAD_GPS.put(isDownloadGpxData());
    197             DOWNLOAD_NOTES.put(isDownloadNotes());
     212            DOWNLOAD_POSSIBILITIES.forEach(type -> type.getBooleanProperty().put(type.getCheckBox().isSelected()));
    198213        }
    199214
    200215        @Override
    201216        public void restoreSettings() {
    202             cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());
    203             cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());
    204             cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());
     217            DOWNLOAD_POSSIBILITIES.forEach(type -> type.getCheckBox().setSelected(type.isEnabled()));
    205218        }
    206219
    207220        @Override
     
    225238             * must be chosen : raw osm data, gpx data, notes.
    226239             * If none of those are selected, then the corresponding dialog is shown to inform the user.
    227240             */
    228             if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
     241            if (DOWNLOAD_POSSIBILITIES.parallelStream().noneMatch(type -> type.isEnabled())) {
     242                StringBuilder line1 = new StringBuilder("<html>");
     243                StringBuilder line2 = new StringBuilder(tr("Please choose to either download"));
     244                DOWNLOAD_POSSIBILITIES.forEach(type -> {
     245                    if (line1.length() == 6) {
     246                        line1.append(tr("Neither"));
     247                    } else {
     248                        line1.append(tr("nor"));
     249                    }
     250                    line1.append(" <strong>").append(type.getCheckBox().getText()).append("</strong> ");
     251                    line2.append(' ').append(type.getCheckBox().getText()).append(tr(", or"));
     252                });
     253                line1.append(tr("is enabled.")).append("<br>");
     254                line2.append(tr(" all.")).append("</html>");
    229255                JOptionPane.showMessageDialog(
    230256                        this.getParent(),
    231                         tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
    232                                         + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
    233                                 cbDownloadOsmData.getText(),
    234                                 cbDownloadGpxData.getText(),
    235                                 cbDownloadNotes.getText()
    236                         ),
     257                        line1.append(line2).toString(),
    237258                        tr("Error"),
    238259                        JOptionPane.ERROR_MESSAGE
    239260                );
     
    250271         * Replies true if the user selected to download OSM data
    251272         *
    252273         * @return true if the user selected to download OSM data
     274         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     275         *             {@code get(0).getCheckBox().isSelected()}
    253276         */
     277        @Deprecated
    254278        public boolean isDownloadOsmData() {
    255             return cbDownloadOsmData.isSelected();
     279            return DOWNLOAD_POSSIBILITIES.get(0).getCheckBox().isSelected();
    256280        }
    257281
    258282        /**
     
    259283         * Replies true if the user selected to download GPX data
    260284         *
    261285         * @return true if the user selected to download GPX data
     286         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     287         *             {@code get(1).getCheckBox().isSelected()}
    262288         */
     289        @Deprecated
    263290        public boolean isDownloadGpxData() {
    264             return cbDownloadGpxData.isSelected();
     291            return DOWNLOAD_POSSIBILITIES.get(1).getCheckBox().isSelected();
    265292        }
    266293
    267294        /**
     
    268295         * Replies true if user selected to download notes
    269296         *
    270297         * @return true if user selected to download notes
     298         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     299         *             {@code get(2).getCheckBox().isSelected()}
    271300         */
     301        @Deprecated
    272302        public boolean isDownloadNotes() {
    273             return cbDownloadNotes.isSelected();
     303            return DOWNLOAD_POSSIBILITIES.get(2).getCheckBox().isSelected();
    274304        }
    275305
    276306        @Override
     
    295325                return;
    296326            }
    297327
    298             boolean isAreaTooLarge = false;
    299             if (!isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    300                 isAreaTooLarge = false;
    301             } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    302                 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
    303                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
    304             } else {
    305                 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
    306                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
    307             }
    308 
    309             displaySizeCheckResult(isAreaTooLarge);
     328            displaySizeCheckResult(DOWNLOAD_POSSIBILITIES.parallelStream()
     329                    .anyMatch(type -> type.isDownloadAreaTooLarge(bbox)));
    310330        }
    311331
    312332        private void displaySizeCheckResult(boolean isAreaTooLarge) {
     
    325345     * Encapsulates data that is required to download from the OSM server.
    326346     */
    327347    static class OSMDownloadData {
    328         private final boolean downloadOSMData;
    329         private final boolean downloadNotes;
    330         private final boolean downloadGPX;
    331348
    332         OSMDownloadData(boolean downloadOSMData, boolean downloadNotes, boolean downloadGPX) {
    333             this.downloadOSMData = downloadOSMData;
    334             this.downloadNotes = downloadNotes;
    335             this.downloadGPX = downloadGPX;
     349        private List<DataDownloadType> downloadPossibilities;
     350
     351        /**
     352         * @param downloadPossibilities A list of DataDownloadTypes (instantiated, with
     353         *                              options set)
     354         */
     355        OSMDownloadData(List<DataDownloadType> downloadPossibilities) {
     356            this.downloadPossibilities = downloadPossibilities;
    336357        }
    337358
    338         boolean isDownloadOSMData() {
    339             return downloadOSMData;
     359        /**
     360         * @return A list of DataDownloadTypes (instantiated, with options set)
     361         */
     362        public List<DataDownloadType> getDownloadPossibilities() {
     363            return downloadPossibilities;
    340364        }
     365    }
    341366
    342         boolean isDownloadNotes() {
    343             return downloadNotes;
     367    /**
     368     * An interface to allow arbitrary download sources and types in the primary
     369     * download window of JOSM
     370     *
     371     * @since xxx
     372     */
     373    interface DataDownloadType {
     374        /**
     375         * @return The checkbox to be added to the UI
     376         */
     377        default JCheckBox getCheckBox() {
     378            return getCheckBox(null);
    344379        }
    345380
    346         boolean isDownloadGPX() {
    347             return downloadGPX;
     381        /**
     382         * @param checkboxChangeListener The listener for checkboxes (may be
     383         *                               {@code null})
     384         * @return The checkbox to be added to the UI
     385         */
     386        JCheckBox getCheckBox(ChangeListener checkboxChangeListener);
     387
     388        /**
     389         * @return The {@link DownloadTask} class which will be getting the data
     390         */
     391        Class<? extends AbstractDownloadTask<?>> getDownloadClass();
     392
     393        /**
     394         * @return The boolean indicating the last state of the download type
     395         */
     396        default boolean isEnabled() {
     397            return getBooleanProperty().get();
    348398        }
     399
     400        /**
     401         * @return The boolean property for this particular download type
     402         */
     403        BooleanProperty getBooleanProperty();
     404
     405        /**
     406         * Check if the area is too large for the current DataDownloadType
     407         *
     408         * @param bound The bound that will be downloaded
     409         * @return {@code true} if we definitely cannot download the area;
     410         */
     411        boolean isDownloadAreaTooLarge(Bounds bound);
    349412    }
     413
     414    private static class OsmDataDownloadType implements DataDownloadType {
     415        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.data", true);
     416        JCheckBox cbDownloadOsmData;
     417
     418        @Override
     419        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     420            if (cbDownloadOsmData == null) {
     421                cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
     422                cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
     423                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     424            }
     425            if (checkboxChangeListener != null) {
     426                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     427            }
     428            return cbDownloadOsmData;
     429        }
     430
     431        @Override
     432        public Class<? extends AbstractDownloadTask<DataSet>> getDownloadClass() {
     433            return DownloadOsmTask.class;
     434        }
     435
     436        @Override
     437        public BooleanProperty getBooleanProperty() {
     438            return IS_ENABLED;
     439        }
     440
     441        @Override
     442        public boolean isDownloadAreaTooLarge(Bounds bound) {
     443            // see max_request_area in
     444            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     445            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
     446        }
     447    }
     448
     449    private static class GpsDataDownloadType implements DataDownloadType {
     450        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.gps", false);
     451        private JCheckBox cbDownloadGpxData;
     452
     453        @Override
     454        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     455            if (cbDownloadGpxData == null) {
     456                cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
     457                cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
     458            }
     459            if (checkboxChangeListener != null) {
     460                cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
     461            }
     462
     463            return cbDownloadGpxData;
     464        }
     465
     466        @Override
     467        public Class<? extends AbstractDownloadTask<GpxData>> getDownloadClass() {
     468            return DownloadGpsTask.class;
     469        }
     470
     471        @Override
     472        public BooleanProperty getBooleanProperty() {
     473            return IS_ENABLED;
     474        }
     475
     476        @Override
     477        public boolean isDownloadAreaTooLarge(Bounds bound) {
     478            return false;
     479        }
     480    }
     481
     482    private static class NotesDataDownloadType implements DataDownloadType {
     483        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.notes", false);
     484        private JCheckBox cbDownloadNotes;
     485
     486        @Override
     487        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     488            if (cbDownloadNotes == null) {
     489                cbDownloadNotes = new JCheckBox(tr("Notes"));
     490                cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
     491            }
     492            if (checkboxChangeListener != null) {
     493                cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
     494            }
     495
     496            return cbDownloadNotes;
     497        }
     498
     499        @Override
     500        public Class<? extends AbstractDownloadTask<NoteData>> getDownloadClass() {
     501            return DownloadNotesTask.class;
     502        }
     503
     504        @Override
     505        public BooleanProperty getBooleanProperty() {
     506            return IS_ENABLED;
     507        }
     508
     509        @Override
     510        public boolean isDownloadAreaTooLarge(Bounds bound) {
     511            // see max_note_request_area in
     512            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     513            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
     514        }
     515    }
     516
    350517}