001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io.remotecontrol.handler;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.ByteArrayInputStream;
007import java.nio.charset.StandardCharsets;
008
009import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
010import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
011import org.openstreetmap.josm.data.osm.DataSet;
012import org.openstreetmap.josm.gui.MainApplication;
013import org.openstreetmap.josm.io.IllegalDataException;
014import org.openstreetmap.josm.io.OsmReader;
015import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
016import org.openstreetmap.josm.tools.Utils;
017
018/**
019 * Handler to load data directly from the URL.
020 * @since 7636
021 */
022public class LoadDataHandler extends RequestHandler {
023
024    private static final String OSM_MIME_TYPE = "application/x-osm+xml";
025
026    /**
027     * The remote control command name used to import data.
028     */
029    public static final String command = "load_data";
030
031    /**
032     * Holds the data input string
033     */
034    private String data;
035
036    /**
037     * Holds the parsed data set
038     */
039    private DataSet dataSet;
040
041    @Override
042    protected void handleRequest() throws RequestHandlerErrorException {
043        MainApplication.worker.submit(new LoadDataTask(getDownloadParams(), dataSet, args.get("layer_name")));
044    }
045
046    @Override
047    public String[] getMandatoryParams() {
048        return new String[]{"data"};
049    }
050
051    @Override
052    public String[] getOptionalParams() {
053        return new String[] {"new_layer", "mime_type", "layer_name", "layer_locked", "download_policy", "upload_policy"};
054    }
055
056    @Override
057    public String getUsage() {
058        return "Reads data encoded directly in the URL and adds it to the current data set";
059    }
060
061    @Override
062    public String[] getUsageExamples() {
063        return new String[]{
064                "/load_data?layer_name=extra_layer&new_layer=true&data=" +
065                    Utils.encodeUrl("<osm version='0.6'><node id='-1' lat='1' lon='2' /></osm>")};
066    }
067
068    @Override
069    public String getPermissionMessage() {
070        return tr("Remote Control has been asked to load the following data:")
071                + "<br>" + data;
072    }
073
074    @Override
075    public PermissionPrefWithDefault getPermissionPref() {
076        // Same permission as the import data, as the difference from a user pov is minimal
077        return PermissionPrefWithDefault.IMPORT_DATA;
078    }
079
080    @Override
081    protected void validateRequest() throws RequestHandlerBadRequestException {
082        validateDownloadParams();
083        this.data = args.get("data");
084        /**
085         * Holds the mime type. Currently only OSM_MIME_TYPE is supported
086         * But it could be extended to text/csv, application/gpx+xml, ... or even binary encoded data
087         */
088        final String mimeType = Utils.firstNonNull(args.get("mime_type"), OSM_MIME_TYPE);
089        try {
090            if (OSM_MIME_TYPE.equals(mimeType)) {
091                final ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
092                dataSet = OsmReader.parseDataSet(in, null);
093            } else {
094                dataSet = new DataSet();
095            }
096        } catch (IllegalDataException e) {
097            throw new RequestHandlerBadRequestException("Failed to parse " + data + ": " + e.getMessage(), e);
098        }
099    }
100
101    protected static class LoadDataTask extends DownloadOsmTask.AbstractInternalTask {
102
103        protected final String layerName;
104
105        /**
106         * Constructs a new {@code LoadDataTask}.
107         * @param settings download settings
108         * @param dataSet data set
109         * @param layerName layer name
110         * @since 13927
111         */
112        public LoadDataTask(DownloadParams settings, DataSet dataSet, String layerName) {
113            super(settings, tr("Loading data"), false, true);
114            this.dataSet = dataSet;
115            this.layerName = layerName;
116        }
117
118        @Override
119        public void realRun() {
120            // No real run, the data is already loaded
121        }
122
123        @Override
124        protected void cancel() {
125            // No Cancel, would be hard without a real run
126        }
127
128        @Override
129        protected void finish() {
130            loadData(layerName, null);
131        }
132    }
133}