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