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.io.InputStream;
008import java.io.UnsupportedEncodingException;
009import java.net.URLDecoder;
010import java.util.HashMap;
011import java.util.Map;
012
013import org.openstreetmap.josm.Main;
014import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
015import org.openstreetmap.josm.data.osm.DataSet;
016import org.openstreetmap.josm.io.OsmReader;
017import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
018
019/**
020 * Handler to load data directly from the URL.
021 * @since 7636
022 */
023public class LoadDataHandler extends RequestHandler {
024
025    private static final String OSM_MIME_TYPE = "application/x-osm+xml";
026
027    /**
028     * The remote control command name used to import data.
029     */
030    public static final String command = "load_data";
031
032    /**
033     * Holds the data input string
034     */
035    private String data;
036
037    /**
038     * Holds the mime type. Currently only OSM_MIME_TYPE is supported
039     * But it could be extended to text/csv, application/gpx+xml, ... or even binary encoded data
040     */
041    private String mimeType;
042
043    @Override
044    protected void handleRequest() throws RequestHandlerErrorException {
045        try {
046            // Transform data string to inputstream
047            InputStream source = new ByteArrayInputStream(data.getBytes("UTF-8"));
048            DataSet dataSet = new DataSet();
049            if (mimeType != null && mimeType.contains(OSM_MIME_TYPE))
050                dataSet = OsmReader.parseDataSet(source, null);
051            Main.worker.submit(new LoadDataTask(isLoadInNewLayer(), dataSet, args.get("layer_name")));
052        } catch (Exception e) {
053            Main.warn("Problem with data: " + data);
054            throw new RequestHandlerErrorException(e);
055        }
056    }
057
058    @Override
059    public String[] getMandatoryParams() {
060        return new String[]{"data"};
061    }
062
063    @Override
064    public String[] getOptionalParams() {
065        return new String[] {"new_layer", "mime_type", "layer_name"};
066    }
067
068    @Override
069    public String getUsage() {
070        return "Reads data encoded directly in the URL and adds it to the current data set";
071    }
072
073    @Override
074    public String[] getUsageExamples() {
075        return new String[] {
076                "/load_data?layer_name=extra_layer&new_layer=true&data=%3Cosm%3E%3Cnode%3E...%3C%2Fnode%3E%3C%2Fosm%3E" };
077    }
078
079    @Override
080    public String getPermissionMessage() {
081        return tr("Remote Control has been asked to load the following data:")
082                + "<br>" + data;
083    }
084
085    @Override
086    public PermissionPrefWithDefault getPermissionPref() {
087        // Same permission as the import data, as the difference from a user pov is minimal
088        return PermissionPrefWithDefault.IMPORT_DATA;
089    }
090
091    @Override
092    protected void parseArgs() {
093        if (request.indexOf('?') == -1)
094            return; // nothing to do
095
096        Map<String, String> args = new HashMap<>();
097
098        // The data itself shouldn't contain any &, = or ? chars.
099        // Those are reserved for the URL parsing
100        // and should be URL encoded as %26, %3D or %3F
101        String query = request.substring(request.indexOf('?') + 1);
102        String[] params = query.split("&");
103        for (String param : params) {
104            String[] kv = param.split("=");
105            if (kv.length == 2)
106                args.put(kv[0], kv[1]);
107        }
108        this.args = args;
109    }
110
111    @Override
112    protected void validateRequest() throws RequestHandlerBadRequestException {
113        if (args.get("data") == null)
114            throw new RequestHandlerBadRequestException("RemoteControl: No data defined in URL");
115        try {
116            data = URLDecoder.decode(args.get("data"), "UTF-8");
117        } catch (UnsupportedEncodingException e) {
118            throw new RequestHandlerBadRequestException("RemoteControl: UnsupportedEncodingException: " + e.getMessage(), e);
119        }
120        mimeType = args.get("mime_type");
121        if (mimeType == null) {
122            mimeType = OSM_MIME_TYPE;
123        }
124    }
125
126    protected class LoadDataTask extends DownloadOsmTask.AbstractInternalTask {
127
128        protected final String layerName;
129
130        public LoadDataTask(boolean newLayer, DataSet dataSet, String layerName) {
131            super(newLayer, tr("Loading data"), false);
132            this.dataSet = dataSet;
133            this.layerName = layerName;
134        }
135
136        @Override
137        public void realRun() {
138            // No real run, the data is already loaded
139        }
140
141        @Override
142        protected void cancel() {
143            // No Cancel, would be hard without a real run
144        }
145
146        @Override
147        protected void finish() {
148            loadData(layerName, null);
149        }
150    }
151}