Ticket #18340: 18340.2.patch

File 18340.2.patch, 22.9 KB (added by taylor.smock, 6 years ago)

Replace OSMDownloadData with List<IDownloadSourceType> (IDownloadSourceType was the DataDownloadType interface, and is now in its own file), rename DOWNLOAD_POSSIBILITIES to DOWNLOAD_SOURCES, change text to use None of, add a method to get a specific instance of a download type (so people can ask for an instance and check if it is enabled), use stream instead of parallelStream.

  • src/org/openstreetmap/josm/gui/download/IDownloadSourceType.java

     
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.download;
     3
     4import javax.swing.JCheckBox;
     5import javax.swing.event.ChangeListener;
     6
     7import org.openstreetmap.josm.actions.downloadtasks.AbstractDownloadTask;
     8import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
     9import org.openstreetmap.josm.data.Bounds;
     10import org.openstreetmap.josm.data.preferences.BooleanProperty;
     11
     12/**
     13 * An interface to allow arbitrary download sources and types in the primary
     14 * download window of JOSM
     15 *
     16 * @since xxx
     17 */
     18interface IDownloadSourceType {
     19    /**
     20     * @return The checkbox to be added to the UI
     21     */
     22    default JCheckBox getCheckBox() {
     23        return getCheckBox(null);
     24    }
     25
     26    /**
     27     * @param checkboxChangeListener The listener for checkboxes (may be
     28     *                               {@code null})
     29     * @return The checkbox to be added to the UI
     30     */
     31    JCheckBox getCheckBox(ChangeListener checkboxChangeListener);
     32
     33    /**
     34     * @return The {@link DownloadTask} class which will be getting the data
     35     */
     36    Class<? extends AbstractDownloadTask<?>> getDownloadClass();
     37
     38    /**
     39     * @return The boolean indicating the last state of the download type
     40     */
     41    default boolean isEnabled() {
     42        return getBooleanProperty().get();
     43    }
     44
     45    /**
     46     * @return The boolean property for this particular download type
     47     */
     48    BooleanProperty getBooleanProperty();
     49
     50    /**
     51     * Check if the area is too large for the current IDownloadSourceType
     52     *
     53     * @param bound The bound that will be downloaded
     54     * @return {@code true} if we definitely cannot download the area;
     55     */
     56    boolean isDownloadAreaTooLarge(Bounds bound);
     57}
  • src/org/openstreetmap/josm/gui/download/OSMDownloadSource.java

     
    88import java.awt.Dimension;
    99import java.awt.Font;
    1010import java.awt.GridBagLayout;
     11import java.lang.reflect.InvocationTargetException;
    1112import java.util.ArrayList;
     13import java.util.Collections;
    1214import java.util.List;
    1315import java.util.concurrent.ExecutionException;
    1416import java.util.concurrent.Future;
     
    2830import org.openstreetmap.josm.data.Bounds;
    2931import org.openstreetmap.josm.data.ProjectionBounds;
    3032import 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;
    3136import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3237import org.openstreetmap.josm.gui.MainApplication;
    3338import org.openstreetmap.josm.gui.MapFrame;
     
    4247 * Class defines the way data is fetched from the OSM server.
    4348 * @since 12652
    4449 */
    45 public class OSMDownloadSource implements DownloadSource<OSMDownloadSource.OSMDownloadData> {
     50public class OSMDownloadSource implements DownloadSource<List<IDownloadSourceType>> {
    4651    /**
    4752     * The simple name for the {@link OSMDownloadSourcePanel}
    4853     * @since 12706
     
    4954     */
    5055    public static final String SIMPLE_NAME = "osmdownloadpanel";
    5156
     57    /** The possible methods to get data */
     58    static final List<IDownloadSourceType> DOWNLOAD_SOURCES = new ArrayList<>();
     59    static {
     60        // Order is important (determines button order, and what gets zoomed to)
     61        DOWNLOAD_SOURCES.add(new OsmDataDownloadType());
     62        DOWNLOAD_SOURCES.add(new GpsDataDownloadType());
     63        DOWNLOAD_SOURCES.add(new NotesDataDownloadType());
     64    }
     65
    5266    @Override
    53     public AbstractDownloadSourcePanel<OSMDownloadData> createPanel(DownloadDialog dialog) {
     67    public AbstractDownloadSourcePanel<List<IDownloadSourceType>> createPanel(DownloadDialog dialog) {
    5468        return new OSMDownloadSourcePanel(this, dialog);
    5569    }
    5670
    5771    @Override
    58     public void doDownload(OSMDownloadData data, DownloadSettings settings) {
     72    public void doDownload(List<IDownloadSourceType> data, DownloadSettings settings) {
    5973        Bounds bbox = settings.getDownloadBounds()
    6074                .orElseThrow(() -> new IllegalArgumentException("OSM downloads requires bounds"));
    6175        boolean zoom = settings.zoomToData();
    6276        boolean newLayer = settings.asNewLayer();
    63         List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
    64 
    65         if (data.isDownloadOSMData()) {
    66             DownloadOsmTask task = new DownloadOsmTask();
    67             task.setZoomAfterDownload(zoom && !data.isDownloadGPX() && !data.isDownloadNotes());
    68             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    69             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    70             if (zoom) {
    71                 tasks.add(new Pair<>(task, future));
     77        final List<Pair<AbstractDownloadTask<?>, Future<?>>> tasks = new ArrayList<>();
     78        IDownloadSourceType zoomTask = zoom ? data.stream().findFirst().orElse(null) : null;
     79        data.stream().filter(IDownloadSourceType::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);
    7291            }
    73         }
     92        });
    7493
    75         if (data.isDownloadGPX()) {
    76             DownloadGpsTask task = new DownloadGpsTask();
    77             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadNotes());
    78             Future<?> future = task.download(new DownloadParams().withNewLayer(newLayer), bbox, null);
    79             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    80             if (zoom) {
    81                 tasks.add(new Pair<>(task, future));
    82             }
    83         }
    84 
    85         if (data.isDownloadNotes()) {
    86             DownloadNotesTask task = new DownloadNotesTask();
    87             task.setZoomAfterDownload(zoom && !data.isDownloadOSMData() && !data.isDownloadGPX());
    88             Future<?> future = task.download(new DownloadParams(), bbox, null);
    89             MainApplication.worker.submit(new PostDownloadHandler(task, future));
    90             if (zoom) {
    91                 tasks.add(new Pair<>(task, future));
    92             }
    93         }
    94 
    9594        if (zoom && tasks.size() > 1) {
    9695            MainApplication.worker.submit(() -> {
    9796                ProjectionBounds bounds = null;
     
    130129    }
    131130
    132131    /**
     132     * @return The possible downloads that JOSM can make in the default Download
     133     *         screen
     134     * @since xxx
     135     */
     136    public List<IDownloadSourceType> getDownloadTypes() {
     137        return Collections.unmodifiableList(DOWNLOAD_SOURCES);
     138    }
     139
     140    /**
     141     * Get the instance of a data download type
     142     *
     143     * @param <T> The type to get
     144     * @param typeClazz The class of the type
     145     * @return The type instance
     146     * @since xxx
     147     */
     148    public <T extends IDownloadSourceType> T getDownloadType(Class<T> typeClazz) {
     149        return DOWNLOAD_SOURCES.stream().filter(typeClazz::isInstance).map(typeClazz::cast).findFirst().orElse(null);
     150    }
     151
     152    /**
     153     * @param type The IDownloadSourceType object to remove
     154     * @return true See {@link List#remove}, but it also returns false if the
     155     * parameter is a class from JOSM core.
     156     * @since xxx
     157     */
     158    public boolean removeDownloadType(IDownloadSourceType type) {
     159        boolean modified = false;
     160        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     161                && !(type instanceof NotesDataDownloadType)) {
     162            modified = DOWNLOAD_SOURCES.remove(type);
     163        }
     164        return modified;
     165    }
     166
     167    /**
     168     * Add a download type to the default JOSM download window
     169     *
     170     * @param type The initialized type to download
     171     * @return See {@link List#add}, but it also returns false if the class
     172     * already has an instance in the list or it is a class from JOSM core.
     173     * @since xxx
     174     */
     175    public boolean addDownloadType(IDownloadSourceType type) {
     176        boolean modified = false;
     177        if (!(type instanceof OsmDataDownloadType) && !(type instanceof GpsDataDownloadType)
     178                && !(type instanceof NotesDataDownloadType)
     179                || DOWNLOAD_SOURCES.stream()
     180                        .noneMatch(possibility -> type.getClass().isInstance(possibility))) {
     181            modified = DOWNLOAD_SOURCES.add(type);
     182        } else {
     183            throw new IllegalArgumentException("There can only be one instance of a class added, and it cannot be a built-in class.");
     184        }
     185        return modified;
     186    }
     187
     188    /**
    133189     * The GUI representation of the OSM download source.
    134190     * @since 12652
    135191     */
    136     public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<OSMDownloadData> {
    137 
    138         private final JCheckBox cbDownloadOsmData;
    139         private final JCheckBox cbDownloadGpxData;
    140         private final JCheckBox cbDownloadNotes;
     192    public static class OSMDownloadSourcePanel extends AbstractDownloadSourcePanel<List<IDownloadSourceType>> {
    141193        private final JLabel sizeCheck = new JLabel();
    142194
    143         private static final BooleanProperty DOWNLOAD_OSM = new BooleanProperty("download.osm.data", true);
    144         private static final BooleanProperty DOWNLOAD_GPS = new BooleanProperty("download.osm.gps", false);
    145         private static final BooleanProperty DOWNLOAD_NOTES = new BooleanProperty("download.osm.notes", false);
    146 
    147195        /**
    148196         * Label used in front of data types available for download. Made public for reuse in other download dialogs.
    149197         * @since 16155
     
    166214
    167215            // adding the download tasks
    168216            add(new JLabel(tr(DATA_SOURCES_AND_TYPES)), GBC.std().insets(5, 5, 1, 5).anchor(GBC.CENTER));
    169             cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
    170             cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
    171             cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
    172 
    173             cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
    174             cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
    175             cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
    176 
    177             cbDownloadNotes = new JCheckBox(tr("Notes"));
    178             cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
    179             cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
    180 
    181217            Font labelFont = sizeCheck.getFont();
    182218            sizeCheck.setFont(labelFont.deriveFont(Font.PLAIN, labelFont.getSize()));
    183219
    184             add(cbDownloadOsmData, GBC.std().insets(1, 5, 1, 5));
    185             add(cbDownloadGpxData, GBC.std().insets(1, 5, 1, 5));
    186             add(cbDownloadNotes, GBC.eol().insets(1, 5, 1, 5));
     220            DOWNLOAD_SOURCES
     221                    .forEach(obj -> add(obj.getCheckBox(checkboxChangeListener), GBC.std().insets(1, 5, 1, 5)));
    187222            add(sizeCheck, GBC.eol().anchor(GBC.EAST).insets(5, 5, 5, 2));
    188223
    189224            setMinimumSize(new Dimension(450, 115));
     
    190225        }
    191226
    192227        @Override
    193         public OSMDownloadData getData() {
    194             return new OSMDownloadData(
    195                     isDownloadOsmData(),
    196                     isDownloadNotes(),
    197                     isDownloadGpxData());
     228        public List<IDownloadSourceType> getData() {
     229            return DOWNLOAD_SOURCES;
    198230        }
    199231
    200232        @Override
    201233        public void rememberSettings() {
    202             DOWNLOAD_OSM.put(isDownloadOsmData());
    203             DOWNLOAD_GPS.put(isDownloadGpxData());
    204             DOWNLOAD_NOTES.put(isDownloadNotes());
     234            DOWNLOAD_SOURCES.forEach(type -> type.getBooleanProperty().put(type.getCheckBox().isSelected()));
    205235        }
    206236
    207237        @Override
    208238        public void restoreSettings() {
    209             cbDownloadOsmData.setSelected(DOWNLOAD_OSM.get());
    210             cbDownloadGpxData.setSelected(DOWNLOAD_GPS.get());
    211             cbDownloadNotes.setSelected(DOWNLOAD_NOTES.get());
     239            DOWNLOAD_SOURCES.forEach(type -> type.getCheckBox().setSelected(type.isEnabled()));
    212240        }
    213241
    214242        @Override
     
    232260             * must be chosen : raw osm data, gpx data, notes.
    233261             * If none of those are selected, then the corresponding dialog is shown to inform the user.
    234262             */
    235             if (!isDownloadOsmData() && !isDownloadGpxData() && !isDownloadNotes()) {
     263            if (DOWNLOAD_SOURCES.stream().noneMatch(IDownloadSourceType::isEnabled)) {
     264                StringBuilder line1 = new StringBuilder("<html>").append(tr("None of"));
     265                StringBuilder line2 = new StringBuilder(tr("Please choose to either download"));
     266
     267                DOWNLOAD_SOURCES.forEach(type -> {
     268                    line1.append(" <strong>").append(type.getCheckBox().getText()).append("</strong> ");
     269                    line2.append(' ').append(type.getCheckBox().getText()).append(tr(", or"));
     270                });
     271                line1.append(tr("is enabled.")).append("<br>");
     272                line2.append(tr(" all.")).append("</html>");
    236273                JOptionPane.showMessageDialog(
    237274                        this.getParent(),
    238                         tr("<html>Neither <strong>{0}</strong> nor <strong>{1}</strong> nor <strong>{2}</strong> is enabled.<br>"
    239                                         + "Please choose to either download OSM data, or GPX data, or Notes, or all.</html>",
    240                                 cbDownloadOsmData.getText(),
    241                                 cbDownloadGpxData.getText(),
    242                                 cbDownloadNotes.getText()
    243                         ),
     275                        line1.append(line2).toString(),
    244276                        tr("Error"),
    245277                        JOptionPane.ERROR_MESSAGE
    246278                );
     
    257289         * Replies true if the user selected to download OSM data
    258290         *
    259291         * @return true if the user selected to download OSM data
     292         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     293         *             {@code get(0).getCheckBox().isSelected()}
    260294         */
     295        @Deprecated
    261296        public boolean isDownloadOsmData() {
    262             return cbDownloadOsmData.isSelected();
     297            return DOWNLOAD_SOURCES.get(0).getCheckBox().isSelected();
    263298        }
    264299
    265300        /**
     
    266301         * Replies true if the user selected to download GPX data
    267302         *
    268303         * @return true if the user selected to download GPX data
     304         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     305         *             {@code get(1).getCheckBox().isSelected()}
    269306         */
     307        @Deprecated
    270308        public boolean isDownloadGpxData() {
    271             return cbDownloadGpxData.isSelected();
     309            return DOWNLOAD_SOURCES.get(1).getCheckBox().isSelected();
    272310        }
    273311
    274312        /**
     
    275313         * Replies true if user selected to download notes
    276314         *
    277315         * @return true if user selected to download notes
     316         * @deprecated since xxx -- use {@link OSMDownloadSource#getDownloadTypes} with
     317         *             {@code get(2).getCheckBox().isSelected()}
    278318         */
     319        @Deprecated
    279320        public boolean isDownloadNotes() {
    280             return cbDownloadNotes.isSelected();
     321            return DOWNLOAD_SOURCES.get(2).getCheckBox().isSelected();
    281322        }
    282323
    283324        @Override
     
    302343                return;
    303344            }
    304345
    305             boolean isAreaTooLarge = false;
    306             if (!isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    307                 isAreaTooLarge = false;
    308             } else if (isDownloadNotes() && !isDownloadOsmData() && !isDownloadGpxData()) {
    309                 // see max_note_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/settings.yml
    310                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
    311             } else {
    312                 // see max_request_area in https://github.com/openstreetmap/openstreetmap-website/blob/master/config/settings.yml
    313                 isAreaTooLarge = bbox.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
    314             }
    315 
    316             displaySizeCheckResult(isAreaTooLarge);
     346            displaySizeCheckResult(DOWNLOAD_SOURCES.stream()
     347                    .anyMatch(type -> type.isDownloadAreaTooLarge(bbox)));
    317348        }
    318349
    319350        private void displaySizeCheckResult(boolean isAreaTooLarge) {
     
    332363     * Encapsulates data that is required to download from the OSM server.
    333364     */
    334365    static class OSMDownloadData {
    335         private final boolean downloadOSMData;
    336         private final boolean downloadNotes;
    337         private final boolean downloadGPX;
    338366
    339         OSMDownloadData(boolean downloadOSMData, boolean downloadNotes, boolean downloadGPX) {
    340             this.downloadOSMData = downloadOSMData;
    341             this.downloadNotes = downloadNotes;
    342             this.downloadGPX = downloadGPX;
     367        private List<IDownloadSourceType> downloadPossibilities;
     368
     369        /**
     370         * @param downloadPossibilities A list of DataDownloadTypes (instantiated, with
     371         *                              options set)
     372         */
     373        OSMDownloadData(List<IDownloadSourceType> downloadPossibilities) {
     374            this.downloadPossibilities = downloadPossibilities;
    343375        }
    344376
    345         boolean isDownloadOSMData() {
    346             return downloadOSMData;
     377        /**
     378         * @return A list of DataDownloadTypes (instantiated, with options set)
     379         */
     380        public List<IDownloadSourceType> getDownloadPossibilities() {
     381            return downloadPossibilities;
    347382        }
     383    }
    348384
    349         boolean isDownloadNotes() {
    350             return downloadNotes;
     385    private static class OsmDataDownloadType implements IDownloadSourceType {
     386        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.data", true);
     387        JCheckBox cbDownloadOsmData;
     388
     389        @Override
     390        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     391            if (cbDownloadOsmData == null) {
     392                cbDownloadOsmData = new JCheckBox(tr("OpenStreetMap data"), true);
     393                cbDownloadOsmData.setToolTipText(tr("Select to download OSM data in the selected download area."));
     394                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     395            }
     396            if (checkboxChangeListener != null) {
     397                cbDownloadOsmData.getModel().addChangeListener(checkboxChangeListener);
     398            }
     399            return cbDownloadOsmData;
    351400        }
    352401
    353         boolean isDownloadGPX() {
    354             return downloadGPX;
     402        @Override
     403        public Class<? extends AbstractDownloadTask<DataSet>> getDownloadClass() {
     404            return DownloadOsmTask.class;
    355405        }
     406
     407        @Override
     408        public BooleanProperty getBooleanProperty() {
     409            return IS_ENABLED;
     410        }
     411
     412        @Override
     413        public boolean isDownloadAreaTooLarge(Bounds bound) {
     414            // see max_request_area in
     415            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     416            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area", 0.25);
     417        }
    356418    }
     419
     420    private static class GpsDataDownloadType implements IDownloadSourceType {
     421        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.gps", false);
     422        private JCheckBox cbDownloadGpxData;
     423
     424        @Override
     425        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     426            if (cbDownloadGpxData == null) {
     427                cbDownloadGpxData = new JCheckBox(tr("Raw GPS data"));
     428                cbDownloadGpxData.setToolTipText(tr("Select to download GPS traces in the selected download area."));
     429            }
     430            if (checkboxChangeListener != null) {
     431                cbDownloadGpxData.getModel().addChangeListener(checkboxChangeListener);
     432            }
     433
     434            return cbDownloadGpxData;
     435        }
     436
     437        @Override
     438        public Class<? extends AbstractDownloadTask<GpxData>> getDownloadClass() {
     439            return DownloadGpsTask.class;
     440        }
     441
     442        @Override
     443        public BooleanProperty getBooleanProperty() {
     444            return IS_ENABLED;
     445        }
     446
     447        @Override
     448        public boolean isDownloadAreaTooLarge(Bounds bound) {
     449            return false;
     450        }
     451    }
     452
     453    private static class NotesDataDownloadType implements IDownloadSourceType {
     454        static final BooleanProperty IS_ENABLED = new BooleanProperty("download.osm.notes", false);
     455        private JCheckBox cbDownloadNotes;
     456
     457        @Override
     458        public JCheckBox getCheckBox(ChangeListener checkboxChangeListener) {
     459            if (cbDownloadNotes == null) {
     460                cbDownloadNotes = new JCheckBox(tr("Notes"));
     461                cbDownloadNotes.setToolTipText(tr("Select to download notes in the selected download area."));
     462            }
     463            if (checkboxChangeListener != null) {
     464                cbDownloadNotes.getModel().addChangeListener(checkboxChangeListener);
     465            }
     466
     467            return cbDownloadNotes;
     468        }
     469
     470        @Override
     471        public Class<? extends AbstractDownloadTask<NoteData>> getDownloadClass() {
     472            return DownloadNotesTask.class;
     473        }
     474
     475        @Override
     476        public BooleanProperty getBooleanProperty() {
     477            return IS_ENABLED;
     478        }
     479
     480        @Override
     481        public boolean isDownloadAreaTooLarge(Bounds bound) {
     482            // see max_note_request_area in
     483            // https://github.com/openstreetmap/openstreetmap-website/blob/master/config/example.application.yml
     484            return bound.getArea() > Config.getPref().getDouble("osm-server.max-request-area-notes", 25);
     485        }
     486    }
    357487}