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}