| | 1 | // License: GPL. For details, see LICENSE file. |
| | 2 | package org.openstreetmap.josm.io.remotecontrol.handler; |
| | 3 | |
| | 4 | import static org.openstreetmap.josm.tools.I18n.tr; |
| | 5 | |
| | 6 | import java.io.ByteArrayInputStream; |
| | 7 | import java.io.InputStream; |
| | 8 | import java.io.UnsupportedEncodingException; |
| | 9 | import java.net.URLDecoder; |
| | 10 | import java.util.HashMap; |
| | 11 | |
| | 12 | import org.openstreetmap.josm.Main; |
| | 13 | import org.openstreetmap.josm.data.osm.DataSet; |
| | 14 | import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; |
| | 15 | import org.openstreetmap.josm.gui.PleaseWaitRunnable; |
| | 16 | import org.openstreetmap.josm.gui.layer.OsmDataLayer; |
| | 17 | import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault; |
| | 18 | import org.openstreetmap.josm.io.OsmReader; |
| | 19 | |
| | 20 | /** |
| | 21 | * Handler to load data directly from the URL |
| | 22 | */ |
| | 23 | public class LoadDataHandler extends RequestHandler { |
| | 24 | |
| | 25 | /** |
| | 26 | * The remote control command name used to import data. |
| | 27 | */ |
| | 28 | public static final String command = "load_data"; |
| | 29 | |
| | 30 | /** |
| | 31 | * Holds the data input string |
| | 32 | */ |
| | 33 | private String data; |
| | 34 | /** |
| | 35 | * Holds the mime type. Currently only text/osm is supported |
| | 36 | * But it could be extended to text/csv, text/gpx, ... or even binary encoded data |
| | 37 | */ |
| | 38 | private String mimeType; |
| | 39 | |
| | 40 | @Override |
| | 41 | protected void handleRequest() throws RequestHandlerErrorException { |
| | 42 | try { |
| | 43 | // Transform data string to inputstream |
| | 44 | InputStream source = new ByteArrayInputStream(data.getBytes("UTF-8")); |
| | 45 | DataSet dataSet = new DataSet(); |
| | 46 | if (mimeType.indexOf("text/osm") == 0) |
| | 47 | dataSet = OsmReader.parseDataSet(source, null); |
| | 48 | LoadDataTask loadDataTask = new LoadDataTask(isLoadInNewLayer(), dataSet, args.get("layer_name")); |
| | 49 | Main.worker.submit(loadDataTask); |
| | 50 | } catch (Exception e) { |
| | 51 | Main.warn("Problem with data: " + data); |
| | 52 | throw new RequestHandlerErrorException(e); |
| | 53 | } |
| | 54 | } |
| | 55 | |
| | 56 | @Override |
| | 57 | public String[] getMandatoryParams() { |
| | 58 | return new String[]{"data"}; |
| | 59 | } |
| | 60 | |
| | 61 | @Override |
| | 62 | public String[] getOptionalParams() { |
| | 63 | return new String[] {"new_layer", "mime_type", "layer_name"}; |
| | 64 | } |
| | 65 | |
| | 66 | @Override |
| | 67 | public String getUsage() { |
| | 68 | return "Reads data encoded directly in the URL and adds it to the current data set"; |
| | 69 | } |
| | 70 | |
| | 71 | @Override |
| | 72 | public String[] getUsageExamples() { |
| | 73 | return new String[] { "/load_data?layer_name=extra_layer&new_layer=true&data=%3Cosm%3E%3Cnode%3E...%3C%2Fnode%3E%3C%2Fosm%3E" }; |
| | 74 | } |
| | 75 | |
| | 76 | @Override |
| | 77 | public String getPermissionMessage() { |
| | 78 | return tr("Remote Control has been asked to load the following data:") |
| | 79 | + "<br>" + data; |
| | 80 | } |
| | 81 | |
| | 82 | @Override |
| | 83 | public PermissionPrefWithDefault getPermissionPref() { |
| | 84 | // Same permission as the import data, as the difference from a user pov is minimal |
| | 85 | return PermissionPrefWithDefault.IMPORT_DATA; |
| | 86 | } |
| | 87 | |
| | 88 | @Override |
| | 89 | protected void parseArgs() { |
| | 90 | if (request.indexOf('?') == -1) |
| | 91 | return; // nothing to do |
| | 92 | |
| | 93 | HashMap<String, String> args = new HashMap<>(); |
| | 94 | |
| | 95 | // The data itself shouldn't contain any &, = or ? chars. |
| | 96 | // Those are reserved for the URL parsing |
| | 97 | // and should be URL encoded as %26, %3D or %3F |
| | 98 | String query = request.substring(request.indexOf('?') + 1); |
| | 99 | String[] params = query.split("&"); |
| | 100 | for (String param : params) |
| | 101 | { |
| | 102 | String[] kv = param.split("="); |
| | 103 | if (kv.length == 2) |
| | 104 | args.put(kv[0], kv[1]); |
| | 105 | } |
| | 106 | this.args = args; |
| | 107 | } |
| | 108 | |
| | 109 | @Override |
| | 110 | protected void validateRequest() throws RequestHandlerBadRequestException { |
| | 111 | if (args.get("data") == null) |
| | 112 | throw new RequestHandlerBadRequestException("RemoteControl: No data defined in URL"); |
| | 113 | try { |
| | 114 | data = URLDecoder.decode(args.get("data"), "UTF-8"); |
| | 115 | } catch (UnsupportedEncodingException e) { |
| | 116 | throw new RequestHandlerBadRequestException("RemoteControl: UnsupportedEncodingException: " + e.getMessage(), e); |
| | 117 | } |
| | 118 | mimeType = args.get("mime_type"); |
| | 119 | if (mimeType == null) |
| | 120 | mimeType = "text/osm"; |
| | 121 | } |
| | 122 | |
| | 123 | public class LoadDataTask extends PleaseWaitRunnable { |
| | 124 | |
| | 125 | protected DataSet dataSet; |
| | 126 | protected boolean newLayer; |
| | 127 | protected String layerName; |
| | 128 | |
| | 129 | public LoadDataTask(boolean newLayer, DataSet dataSet, String layerName) { |
| | 130 | super(tr("Loading data"), false); |
| | 131 | this.dataSet = dataSet; |
| | 132 | this.newLayer = newLayer; |
| | 133 | this.layerName = layerName; |
| | 134 | } |
| | 135 | |
| | 136 | @Override public void realRun() { |
| | 137 | // No real run, the data is already loaded |
| | 138 | } |
| | 139 | |
| | 140 | @Override protected void cancel() { |
| | 141 | // No Cancel, would be hard without a real run |
| | 142 | } |
| | 143 | |
| | 144 | @Override protected void finish() { |
| | 145 | OsmDataLayer layer = getEditLayer(); |
| | 146 | if (newLayer || layer == null) { |
| | 147 | layer = createNewLayer(layerName, dataSet); |
| | 148 | final boolean isDisplayingMapView = Main.isDisplayingMapView(); |
| | 149 | |
| | 150 | Main.main.addLayer(layer); |
| | 151 | // If the mapView is not there yet, we cannot calculate the bounds (see constructor of MapView). |
| | 152 | // Otherwise jump to the current download. |
| | 153 | if (isDisplayingMapView) { |
| | 154 | computeBboxAndCenterScale(dataSet); |
| | 155 | } |
| | 156 | } else { |
| | 157 | layer.mergeFrom(dataSet); |
| | 158 | computeBboxAndCenterScale(dataSet); |
| | 159 | // Mark the layer as changed |
| | 160 | layer.onPostDownloadFromServer(); |
| | 161 | } |
| | 162 | |
| | 163 | } |
| | 164 | |
| | 165 | protected OsmDataLayer getEditLayer() { |
| | 166 | if (!Main.isDisplayingMapView()) return null; |
| | 167 | return Main.main.getEditLayer(); |
| | 168 | } |
| | 169 | |
| | 170 | protected void computeBboxAndCenterScale(DataSet data) { |
| | 171 | BoundingXYVisitor v = new BoundingXYVisitor(); |
| | 172 | v.computeBoundingBox(data.getNodes()); |
| | 173 | Main.map.mapView.recalculateCenterScale(v); |
| | 174 | } |
| | 175 | |
| | 176 | protected OsmDataLayer createNewLayer(String layerName, DataSet data) { |
| | 177 | if (layerName == null || layerName.isEmpty()) { |
| | 178 | layerName = OsmDataLayer.createNewName(); |
| | 179 | } |
| | 180 | return new OsmDataLayer(data, layerName, null); |
| | 181 | } |
| | 182 | } |
| | 183 | } |