Ticket #22034: remote-control.patch

File remote-control.patch, 58.6 KB (added by hiddewie, 4 years ago)

patch v1

  • src/org/openstreetmap/josm/io/remotecontrol/RemoteControlHttpServer.java

     
    88import java.net.Socket;
    99import java.net.SocketException;
    1010
     11import org.openstreetmap.josm.data.preferences.IntegerProperty;
    1112import org.openstreetmap.josm.spi.preferences.Config;
    1213import org.openstreetmap.josm.tools.Logging;
    1314
     
    1920 */
    2021public class RemoteControlHttpServer extends Thread {
    2122
     23    /**
     24     * preference to define remote control port
     25     */
     26    public static final IntegerProperty PORT = new IntegerProperty("remote.control.port", 8111);
     27
    2228    /** The server socket */
    2329    private final ServerSocket server;
    2430
     
    3238     */
    3339    public static void restartRemoteControlHttpServer() {
    3440        stopRemoteControlHttpServer();
    35         int port = Config.getPref().getInt("remote.control.port", 8111);
     41        int port = PORT.get();
    3642        try {
    3743            instance4 = new RemoteControlHttpServer(port, false);
    3844            instance4.start();
  • src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java

     
    1313import java.util.Collection;
    1414import java.util.Date;
    1515import java.util.HashMap;
     16import java.util.List;
    1617import java.util.Locale;
    1718import java.util.Map;
    1819import java.util.Map.Entry;
     
    2223import java.util.TreeMap;
    2324import java.util.regex.Matcher;
    2425import java.util.regex.Pattern;
     26import java.util.stream.Collectors;
    2527import java.util.stream.Stream;
    2628
    2729import javax.json.Json;
     
    4446import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerForbiddenException;
    4547import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerOsmApiException;
    4648import org.openstreetmap.josm.io.remotecontrol.handler.VersionHandler;
     49import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    4750import org.openstreetmap.josm.tools.Logging;
    4851import org.openstreetmap.josm.tools.Utils;
    4952
     
    450453        StringBuilder usage = new StringBuilder(1024);
    451454        for (Entry<String, Class<? extends RequestHandler>> handler : handlers.entrySet()) {
    452455            RequestHandler sample = handler.getValue().getConstructor().newInstance();
    453             String[] mandatory = sample.getMandatoryParams();
    454             String[] optional = sample.getOptionalParams();
     456            List<String> mandatory = sample.getParameters().stream().filter(RequestParameter::isMandatory).map(RequestParameter::getName).sorted().collect(Collectors.toList());
     457            List<String> optional = sample.getParameters().stream().filter(RequestParameter::isOptional).map(RequestParameter::getName).sorted().collect(Collectors.toList());
    455458            String[] examples = sample.getUsageExamples(handler.getKey().substring(1));
    456459            usage.append("<li>")
    457460                 .append(handler.getKey());
     
    458461            if (!Utils.isEmpty(sample.getUsage())) {
    459462                usage.append(" &mdash; <i>").append(sample.getUsage()).append("</i>");
    460463            }
    461             if (mandatory != null && mandatory.length > 0) {
     464            if (!mandatory.isEmpty()) {
    462465                usage.append("<br/>mandatory parameters: ").append(String.join(", ", mandatory));
    463466            }
    464             if (optional != null && optional.length > 0) {
     467            if (!optional.isEmpty()) {
    465468                usage.append("<br/>optional parameters: ").append(String.join(", ", optional));
    466469            }
    467470            if (examples != null && examples.length > 0) {
    468471                usage.append("<br/>examples: ");
    469472                for (String ex: examples) {
    470                     usage.append("<br/> <a href=\"http://localhost:8111").append(ex).append("\">").append(ex).append("</a>");
     473                    usage.append("<br/> <a href=\"http://localhost:" + RemoteControlHttpServer.PORT.get()).append(ex).append("\">").append(ex).append("</a>");
    471474                }
    472475            }
    473476            usage.append("</li>");
  • src/org/openstreetmap/josm/io/remotecontrol/handler/AddNodeHandler.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    66import java.awt.Point;
     7import java.util.Arrays;
    78import java.util.Collections;
     9import java.util.List;
    810
    911import org.openstreetmap.josm.actions.AutoScaleAction;
    1012import org.openstreetmap.josm.actions.AutoScaleAction.AutoScaleMode;
     
    1921import org.openstreetmap.josm.gui.util.GuiHelper;
    2022import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
    2123import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     24import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    2225import org.openstreetmap.josm.spi.preferences.Config;
    2326import org.openstreetmap.josm.tools.Logging;
    2427
     
    3235     */
    3336    public static final String command = "add_node";
    3437
     38    private final RequestParameter<Double> latParameter = RequestParameter.mandatory("lat", Double::parseDouble, "number");
     39    private final RequestParameter<Double> lonParameter = RequestParameter.mandatory("lon", Double::parseDouble, "number");
     40
    3541    private double lat;
    3642    private double lon;
    3743
     
    4147    }
    4248
    4349    @Override
    44     public String[] getMandatoryParams() {
    45         return new String[] {"lat", "lon"};
     50    public List<RequestParameter<?>> getParameters() {
     51        return Arrays.asList(
     52            latParameter,
     53            lonParameter,
     54            addTagsParameter
     55        );
    4656    }
    4757
    4858    @Override
    49     public String[] getOptionalParams() {
    50         return new String[] {"addtags"};
    51     }
    52 
    53     @Override
    5459    public String getUsage() {
    55         return "adds a node (given by its latitude and longitude) to the current dataset";
     60        return "Adds a node (given by its latitude and longitude) to the current dataset";
    5661    }
    5762
    5863    @Override
     
    6671    @Override
    6772    public String getPermissionMessage() {
    6873        return tr("Remote Control has been asked to create a new node.") +
    69                 "<br>" + tr("Coordinates: ") + args.get("lat") + ", " + args.get("lon");
     74                "<br>" + tr("Coordinates: ") + latParameter.read(args) + ", " + lonParameter.read(args);
    7075    }
    7176
    7277    @Override
     
    116121    @Override
    117122    protected void validateRequest() throws RequestHandlerBadRequestException {
    118123        try {
    119             lat = Double.parseDouble(args != null ? args.get("lat") : "");
    120             lon = Double.parseDouble(args != null ? args.get("lon") : "");
     124            lat = latParameter.read(args);
     125            lon = lonParameter.read(args);
    121126        } catch (NumberFormatException e) {
    122127            throw new RequestHandlerBadRequestException("NumberFormatException ("+e.getMessage()+')', e);
    123128        }
     129
    124130        if (MainApplication.getLayerManager().getEditLayer() == null) {
    125131             throw new RequestHandlerBadRequestException(tr("There is no layer opened to add node"));
    126132        }
  • src/org/openstreetmap/josm/io/remotecontrol/handler/AddWayHandler.java

     
    2929import org.openstreetmap.josm.gui.util.GuiHelper;
    3030import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
    3131import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     32import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    3233import org.openstreetmap.josm.spi.preferences.Config;
    3334
    3435/**
     
    4142     */
    4243    public static final String command = "add_way";
    4344
     45    private final RequestParameter<String> wayParameter = RequestParameter.mandatory("way");
     46
    4447    private final List<LatLon> allCoordinates = new ArrayList<>();
    4548
    4649    /**
     
    4952    private Map<LatLon, Node> addedNodes;
    5053
    5154    @Override
    52     public String[] getMandatoryParams() {
    53         return new String[]{"way"};
     55    public List<RequestParameter<?>> getParameters() {
     56        return Arrays.asList(
     57            wayParameter,
     58            addTagsParameter
     59        );
    5460    }
    5561
    5662    @Override
    57     public String[] getOptionalParams() {
    58         return new String[] {"addtags"};
    59     }
    60 
    61     @Override
    6263    public String getUsage() {
    63         return "adds a way (given by a semicolon separated sequence of lat,lon pairs) to the current dataset";
     64        return "Adds a way (given by a semicolon separated sequence of lat,lon pairs) to the current dataset";
    6465    }
    6566
    6667    @Override
     
    9192    @Override
    9293    protected void validateRequest() throws RequestHandlerBadRequestException {
    9394        allCoordinates.clear();
    94         for (String coordinatesString : splitArg("way", SPLITTER_SEMIC)) {
     95        for (String coordinatesString : SPLITTER_SEMIC.split(wayParameter.read(args), -1)) {
    9596            String[] coordinates = coordinatesString.split(",\\s*", 2);
    9697            if (coordinates.length < 2) {
    9798                throw new RequestHandlerBadRequestException(
  • src/org/openstreetmap/josm/io/remotecontrol/handler/FeaturesHandler.java

     
    55
    66import java.util.Arrays;
    77import java.util.Collection;
     8import java.util.List;
    89import java.util.stream.Collectors;
     10import java.util.stream.Stream;
    911
    1012import javax.json.Json;
    1113import javax.json.JsonArray;
     
    1517
    1618import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
    1719import org.openstreetmap.josm.io.remotecontrol.RequestProcessor;
     20import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    1821
    1922/**
    2023 * Reports available commands, their parameters and examples
     
    2730     */
    2831    public static final String command = "features";
    2932
     33    private static final RequestParameter<String> jsonpParameter = RequestParameter.optional("jsonp");
     34    private static final RequestParameter<String> queryParameter = RequestParameter.optional("q");
     35
    3036    @Override
    3137    protected void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException {
    32         String q = args.get("q");
     38        String q = queryParameter.read(args);
    3339        Collection<String> handlers = q == null ? null : Arrays.asList(q.split("[,\\s]+", -1));
    3440        content = getHandlersInfoAsJSON(handlers).toString();
    3541        contentType = "application/json";
    36         if (args.containsKey("jsonp")) {
    37             content = args.get("jsonp") + " && " + args.get("jsonp") + '(' + content + ')';
     42        if (jsonpParameter.isPresent(args)) {
     43            content = jsonpParameter.read(args) + " && " + jsonpParameter.read(args) + '(' + content + ')';
    3844        }
    3945    }
    4046
     
    5258        if (handler.getUsage() != null) {
    5359            json.add("usage", handler.getUsage());
    5460        }
    55         json.add("parameters", toJsonArray(handler.getMandatoryParams()));
    56         json.add("optional", toJsonArray(handler.getOptionalParams()));
    57         json.add("examples", toJsonArray(handler.getUsageExamples(handler.getCommand())));
     61        json.add("parameters", toJsonArray(handler.getParameters().stream().filter(RequestParameter::isMandatory).map(RequestParameter::getName)));
     62        json.add("optional", toJsonArray(handler.getParameters().stream().filter(RequestParameter::isOptional).map(RequestParameter::getName)));
     63        json.add("examples", toJsonArray(Arrays.stream(handler.getUsageExamples(handler.getCommand()))));
    5864        return json.build();
    5965    }
    6066
    61     private static JsonArray toJsonArray(String[] strings) {
    62         return Arrays.stream(strings)
     67    private static JsonArray toJsonArray(Stream<String> strings) {
     68        return strings
    6369                .collect(Collectors.collectingAndThen(Collectors.toList(), Json::createArrayBuilder))
    6470                .build();
    6571    }
     
    7581    }
    7682
    7783    @Override
    78     public String[] getMandatoryParams() {
    79         return new String[0];
     84    public List<RequestParameter<?>> getParameters() {
     85        return Arrays.asList(
     86            jsonpParameter,
     87            queryParameter
     88        );
    8089    }
    8190
    8291    @Override
    83     public String[] getOptionalParams() {
    84         return new String[]{"jsonp", "q"};
    85     }
    86 
    87     @Override
    8892    protected void validateRequest() throws RequestHandlerBadRequestException {
    8993        // Nothing to do
    9094    }
     
    9195
    9296    @Override
    9397    public String getUsage() {
    94         return "reports available commands, their parameters and examples";
     98        return "Reports available commands, their parameters and examples";
    9599    }
    96100
    97101    @Override
    98102    public String[] getUsageExamples() {
    99         return new String[] {"/features", "/features?q=import,add_node"};
     103        return new String[] {
     104            "/features",
     105            "/features?q=import,add_node"
     106        };
    100107    }
    101108}
  • src/org/openstreetmap/josm/io/remotecontrol/handler/ImageryHandler.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.util.ArrayList;
    67import java.util.Arrays;
    78import java.util.LinkedHashSet;
     9import java.util.List;
    810import java.util.Map;
    911import java.util.Objects;
    1012import java.util.Set;
     13import java.util.stream.Collectors;
    1114
    1215import org.openstreetmap.josm.data.StructUtils;
    1316import org.openstreetmap.josm.data.imagery.ImageryInfo;
     
    1821import org.openstreetmap.josm.gui.layer.ImageryLayer;
    1922import org.openstreetmap.josm.gui.util.GuiHelper;
    2023import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     24import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    2125import org.openstreetmap.josm.tools.CheckParameterUtil;
    2226import org.openstreetmap.josm.tools.Logging;
    2327import org.openstreetmap.josm.tools.Utils;
     
    3337     */
    3438    public static final String command = "imagery";
    3539
     40    private final RequestParameter<String> urlParameter = RequestParameter.optional("url");
     41    private final RequestParameter<String> idParameters = RequestParameter.optional("id");
     42
    3643    @Override
    3744    public String getPermissionMessage() {
    3845        return tr("Remote Control has been asked to load an imagery layer from the following URL:")
     
    4047    }
    4148
    4249    @Override
    43     public String[] getMandatoryParams() {
    44         return new String[0];
    45     }
     50    public List<RequestParameter<?>> getParameters() {
     51        Map<String, String> struct = StructUtils.serializeStruct(new ImageryPreferenceEntry(), ImageryPreferenceEntry.class,
     52            StructUtils.SerializeOptions.INCLUDE_NULL, StructUtils.SerializeOptions.INCLUDE_DEFAULT);
    4653
    47     @Override
    48     public String[] getOptionalParams() {
    49         Set<String> params = new LinkedHashSet<>();
    50         params.add("url");
    51         params.add("id");
    52         Map<String, String> struct = StructUtils.serializeStruct(new ImageryPreferenceEntry(), ImageryPreferenceEntry.class,
    53                 StructUtils.SerializeOptions.INCLUDE_NULL, StructUtils.SerializeOptions.INCLUDE_DEFAULT);
    54         params.addAll(struct.keySet());
    55         return params.toArray(new String[0]);
     54        List<RequestParameter<?>> imageryPreferenceParameters = struct.keySet().stream()
     55            .map(RequestParameter::optional)
     56            .filter(param -> !param.getName().equals("url") && !param.getName().equals("id"))
     57            .collect(Collectors.toList());
     58
     59        List<RequestParameter<?>> parameters = new ArrayList<>();
     60        parameters.add(urlParameter);
     61        parameters.add(idParameters);
     62        parameters.addAll(imageryPreferenceParameters);
     63        return parameters;
    5664    }
    5765
    5866    @Override
     
    6169    }
    6270
    6371    protected ImageryInfo buildImageryInfo() {
    64         String id = args.get("id");
     72        String id = idParameters.read(args);
    6573        if (id != null) {
    6674            return ImageryLayerInfo.instance.getAllDefaultLayers().stream()
    6775                    .filter(l -> Objects.equals(l.getId(), id))
     
    108116
    109117    @Override
    110118    public String getUsage() {
    111         return "adds an imagery layer (e.g. WMS, TMS)";
     119        return "Adds an imagery layer (e.g. WMS, TMS)";
    112120    }
    113121
    114122    @Override
     
    119127            "/imagery?id=Bing",
    120128            "/imagery?title=osm&type=tms&url=https://a.tile.openstreetmap.org/%7Bzoom%7D/%7Bx%7D/%7By%7D.png",
    121129            "/imagery?title=landsat&type=wms&url=http://irs.gis-lab.info/?" +
    122                     "layers=landsat&SRS=%7Bproj%7D&WIDTH=%7Bwidth%7D&HEIGHT=%7Bheight%7D&BBOX=%7Bbbox%7D",
    123             "/imagery?title=...&type={"+types+"}&url=....[&cookies=...][&min_zoom=...][&max_zoom=...]"
    124             };
     130                "layers=landsat&SRS=%7Bproj%7D&WIDTH=%7Bwidth%7D&HEIGHT=%7Bheight%7D&BBOX=%7Bbbox%7D",
     131            "/imagery?title=...&type={" + types + "}&url=....[&cookies=...][&min_zoom=...][&max_zoom=...]"
     132        };
    125133    }
    126134}
  • src/org/openstreetmap/josm/io/remotecontrol/handler/ImportHandler.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.lang.reflect.Array;
    67import java.net.MalformedURLException;
    78import java.net.URL;
     9import java.util.Arrays;
    810import java.util.Collection;
     11import java.util.Collections;
    912import java.util.LinkedHashSet;
     13import java.util.List;
    1014import java.util.Set;
    1115
    1216import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
     
    1317import org.openstreetmap.josm.actions.downloadtasks.DownloadTask;
    1418import org.openstreetmap.josm.gui.MainApplication;
    1519import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     20import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    1621import org.openstreetmap.josm.spi.preferences.Config;
    1722import org.openstreetmap.josm.tools.Logging;
    1823import org.openstreetmap.josm.tools.Utils;
     
    2732     */
    2833    public static final String command = "import";
    2934
     35    private final RequestParameter<String> urlParameter = RequestParameter.mandatory("url");
     36
    3037    private URL url;
    3138    private Collection<DownloadTask> suitableDownloadTasks;
    3239
     
    4855                    task.loadUrl(getDownloadParams(), url.toExternalForm(), null);
    4956                }
    5057            }
    51             LoadAndZoomHandler.parseChangesetTags(args);
     58            LoadAndZoomHandler.parseChangesetTags(LoadAndZoomHandler.changesetTagsParameter, args);
    5259        } catch (RuntimeException ex) { // NOPMD
    5360            Logging.warn("RemoteControl: Error parsing import remote control request:");
    5461            Logging.error(ex);
     
    5764    }
    5865
    5966    @Override
    60     public String[] getMandatoryParams() {
    61         return new String[]{"url"};
     67    public List<RequestParameter<?>> getParameters() {
     68        return Arrays.asList(
     69            urlParameter,
     70            newLayerParameter,
     71            layerNameParameter,
     72            layerLockedParameter,
     73            downloadPolicyParameter,
     74            uploadPolicyParameter,
     75            LoadAndZoomHandler.changesetTagsParameter
     76        );
    6277    }
    6378
    6479    @Override
    65     public String[] getOptionalParams() {
    66         return new String[] {"new_layer", "layer_name", "layer_locked", "download_policy", "upload_policy", "changeset_tags"};
    67     }
    68 
    69     @Override
    7080    public String getUsage() {
    71         return "downloads the specified OSM file and adds it to the current data set";
     81        return "Downloads the specified OSM file and adds it to the current data set";
    7282    }
    7383
    7484    @Override
    7585    public String[] getUsageExamples() {
    76         return new String[] {"/import?url=" + Utils.encodeUrl(
    77                 Config.getUrls().getJOSMWebsite()+"/browser/josm/trunk/nodist/data/direction-arrows.osm?format=txt")};
     86        return new String[] {
     87            "/import?url=" + Utils.encodeUrl(
     88                Config.getUrls().getJOSMWebsite()+"/browser/josm/trunk/nodist/data/direction-arrows.osm?format=txt")
     89        };
    7890    }
    7991
    8092    @Override
     
    102114    @Override
    103115    protected void validateRequest() throws RequestHandlerBadRequestException {
    104116        validateDownloadParams();
    105         String urlString = args != null ? args.get("url") : null;
     117        String urlString = urlParameter.read(args);
    106118        if (Config.getPref().getBoolean("remotecontrol.importhandler.fix_url_query", true)) {
    107119            urlString = Utils.fixURLQuery(urlString);
    108120        }
  • src/org/openstreetmap/josm/io/remotecontrol/handler/LoadAndZoomHandler.java

     
    99import java.util.Collection;
    1010import java.util.Collections;
    1111import java.util.LinkedHashSet;
     12import java.util.List;
    1213import java.util.Map;
    1314import java.util.Set;
    1415import java.util.concurrent.ExecutionException;
     
    4243import org.openstreetmap.josm.io.OsmTransferException;
    4344import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
    4445import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     46import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    4547import org.openstreetmap.josm.tools.Logging;
    4648import org.openstreetmap.josm.tools.SubclassFilteredCollection;
    4749import org.openstreetmap.josm.tools.Utils;
     
    6365    public static final String command2 = "zoom";
    6466    private static final String CURRENT_SELECTION = "currentselection";
    6567
     68    private final RequestParameter<Double> bottomParameter = RequestParameter.mandatory("bottom", Double::parseDouble, "number");
     69    private final RequestParameter<Double> topParameter = RequestParameter.mandatory("top", Double::parseDouble, "number");
     70    private final RequestParameter<Double> leftParameter = RequestParameter.mandatory("left", Double::parseDouble, "number");
     71    private final RequestParameter<Double> rightParameter = RequestParameter.mandatory("right", Double::parseDouble, "number");
     72    private final RequestParameter<String> selectParameter = RequestParameter.optional("select");
     73    private final RequestParameter<String> searchParameter = RequestParameter.optional("search");
     74    private final RequestParameter<String> zoomModeParameter = RequestParameter.optional("zoom_mode");
     75    private final RequestParameter<String> changesetCommentParameter = RequestParameter.optional("changeset_comment");
     76    private final RequestParameter<String> changesetSourceParameter = RequestParameter.optional("changeset_source");
     77    private final RequestParameter<String> changesetHashtagsParameter = RequestParameter.optional("changeset_hashtags");
     78    public static final RequestParameter<String> changesetTagsParameter = RequestParameter.optional("changeset_tags");
     79
    6680    // Mandatory arguments
    6781    private double minlat;
    6882    private double maxlat;
     
    7892    public String getPermissionMessage() {
    7993        String msg = tr("Remote Control has been asked to load data from the API.") +
    8094                "<br>" + tr("Bounding box: ") + new BBox(minlon, minlat, maxlon, maxlat).toStringCSV(", ");
    81         if (args.containsKey("select") && !toSelect.isEmpty()) {
     95        if (selectParameter.isPresent(args) && !toSelect.isEmpty()) {
    8296            msg += "<br>" + tr("Selection: {0}", toSelect.size());
    8397        }
    8498        return msg;
     
    8599    }
    86100
    87101    @Override
    88     public String[] getMandatoryParams() {
    89         return new String[] {"bottom", "top", "left", "right"};
     102    public List<RequestParameter<?>> getParameters() {
     103        return Arrays.asList(
     104            bottomParameter,
     105            topParameter,
     106            leftParameter,
     107            rightParameter,
     108            newLayerParameter,
     109            layerNameParameter,
     110            addTagsParameter,
     111            selectParameter,
     112            zoomModeParameter,
     113            changesetCommentParameter,
     114            changesetSourceParameter,
     115            changesetHashtagsParameter,
     116            changesetTagsParameter,
     117            searchParameter,
     118            layerLockedParameter,
     119            downloadPolicyParameter,
     120            uploadPolicyParameter
     121        );
    90122    }
    91123
    92124    @Override
    93     public String[] getOptionalParams() {
    94         return new String[] {"new_layer", "layer_name", "addtags", "select", "zoom_mode",
    95                 "changeset_comment", "changeset_source", "changeset_hashtags", "changeset_tags",
    96                 "search", "layer_locked", "download_policy", "upload_policy"};
    97     }
    98 
    99     @Override
    100125    public String getUsage() {
    101         return "download a bounding box from the API, zoom to the downloaded area and optionally select one or more objects";
     126        return "Download a bounding box from the API, zoom to the downloaded area and optionally select one or more objects";
    102127    }
    103128
    104129    @Override
     
    110135    public String[] getUsageExamples(String cmd) {
    111136        if (command.equals(cmd)) {
    112137            return new String[] {
    113                     "/load_and_zoom?addtags=wikipedia:de=Wei%C3%9Fe_Gasse|maxspeed=5&select=way23071688,way23076176,way23076177," +
    114                             "&left=13.740&right=13.741&top=51.05&bottom=51.049",
    115                     "/load_and_zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999&new_layer=true"};
     138                "/load_and_zoom?addtags=wikipedia:de=Wei%C3%9Fe_Gasse|maxspeed=5&select=way23071688,way23076176,way23076177," +
     139                    "&left=13.740&right=13.741&top=51.05&bottom=51.049",
     140                "/load_and_zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999&new_layer=true"
     141            };
    116142        } else {
    117143            return new String[] {
    118             "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999",
    119             "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&search=highway+OR+railway",
    120             "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&search=" + CURRENT_SELECTION + "&addtags=foo=bar",
     144                "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&select=node413602999",
     145                "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&search=highway+OR+railway",
     146                "/zoom?left=8.19&right=8.20&top=48.605&bottom=48.590&search=" + CURRENT_SELECTION + "&addtags=foo=bar"
    121147            };
    122148        }
    123149    }
     
    185211        /**
    186212         * deselect objects if parameter addtags given
    187213         */
    188         if (args.containsKey("addtags") && !isKeepingCurrentSelection) {
     214        if (addTagsParameter.isPresent(args) && !isKeepingCurrentSelection) {
    189215            GuiHelper.executeByMainWorkerInEDT(() -> {
    190216                DataSet ds = MainApplication.getLayerManager().getEditDataSet();
    191217                if (ds == null) // e.g. download failed
     
    196222
    197223        final Collection<OsmPrimitive> forTagAdd = new LinkedHashSet<>();
    198224        final Bounds bbox = new Bounds(minlat, minlon, maxlat, maxlon);
    199         if (args.containsKey("select") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
     225        if (selectParameter.isPresent(args) && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
    200226            // select objects after downloading, zoom to selection.
    201227            GuiHelper.executeByMainWorkerInEDT(() -> {
    202228                Set<OsmPrimitive> newSel = new LinkedHashSet<>();
     
    225251                    map.relationListDialog.selectRelations(Utils.filteredCollection(newSel, Relation.class));
    226252                }
    227253            });
    228         } else if (args.containsKey("search") && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
     254        } else if (selectParameter.isPresent(args) && PermissionPrefWithDefault.CHANGE_SELECTION.isAllowed()) {
    229255            try {
    230                 final SearchCompiler.Match search = SearchCompiler.compile(args.get("search"));
     256                final SearchCompiler.Match search = SearchCompiler.compile(selectParameter.read(args));
    231257                MainApplication.worker.submit(() -> {
    232258                    final DataSet ds = MainApplication.getLayerManager().getEditDataSet();
    233259                    final Collection<OsmPrimitive> filteredPrimitives = SubclassFilteredCollection.filter(ds.allPrimitives(), search);
     
    245271        }
    246272
    247273        // This comes before the other changeset tags, so that they can be overridden
    248         parseChangesetTags(args);
     274        parseChangesetTags(changesetTagsParameter, args);
    249275
    250276        // add changeset tags after download if necessary
    251         if (args.containsKey("changeset_comment") || args.containsKey("changeset_source") || args.containsKey("changeset_hashtags")) {
     277        if (changesetCommentParameter.isPresent(args) || changesetSourceParameter.isPresent(args) || changesetHashtagsParameter.isPresent(args)) {
    252278            MainApplication.worker.submit(() -> {
    253279                DataSet ds = MainApplication.getLayerManager().getEditDataSet();
    254280                if (ds != null) {
    255                     for (String tag : Arrays.asList("changeset_comment", "changeset_source", "changeset_hashtags")) {
    256                         if (args.containsKey(tag)) {
    257                             final String tagKey = tag.substring("changeset_".length());
    258                             final String value = args.get(tag);
    259                             if (!Utils.isStripEmpty(value)) {
    260                                 ds.addChangeSetTag(tagKey, value);
    261                             } else {
    262                                 ds.addChangeSetTag(tagKey, null);
    263                             }
    264                         }
     281                    if (changesetCommentParameter.isPresent(args)) {
     282                        ds.addChangeSetTag("comment", changesetCommentParameter.read(args));
    265283                    }
     284                    if (changesetSourceParameter.isPresent(args)) {
     285                        ds.addChangeSetTag("source", changesetSourceParameter.read(args));
     286                    }
     287                    if (changesetHashtagsParameter.isPresent(args)) {
     288                        ds.addChangeSetTag("hashtags", changesetHashtagsParameter.read(args));
     289                    }
    266290                }
    267291            });
    268292        }
    269293
    270294        // add tags to objects
    271         if (args.containsKey("addtags")) {
     295        if (addTagsParameter.isPresent(args)) {
    272296            // needs to run in EDT since forTagAdd is updated in EDT as well
    273297            GuiHelper.executeByMainWorkerInEDT(() -> {
    274298                if (!forTagAdd.isEmpty()) {
     
    288312        }
    289313    }
    290314
    291     static void parseChangesetTags(Map<String, String> args) {
    292         if (args.containsKey("changeset_tags")) {
     315    static void parseChangesetTags(RequestParameter<String> changesetTagsParameter, Map<String, String> args) {
     316        if (changesetTagsParameter.isPresent(args)) {
    293317            MainApplication.worker.submit(() -> {
    294318                DataSet ds = MainApplication.getLayerManager().getEditDataSet();
    295319                if (ds != null) {
    296                     AddTagsDialog.parseUrlTagsToKeyValues(args.get("changeset_tags")).forEach(ds::addChangeSetTag);
     320                    AddTagsDialog.parseUrlTagsToKeyValues(changesetTagsParameter.read(args)).forEach(ds::addChangeSetTag);
    297321                }
    298322            });
    299323        }
     
    304328            return;
    305329        }
    306330        // zoom_mode=(download|selection), defaults to selection
    307         if (!"download".equals(args.get("zoom_mode")) && !primitives.isEmpty()) {
     331        if (!"download".equals(zoomModeParameter.read(args)) && !primitives.isEmpty()) {
    308332            AutoScaleAction.autoScale(AutoScaleMode.SELECTION);
    309333        } else if (MainApplication.isDisplayingMapView()) {
    310334            // make sure this isn't called unless there *is* a MapView
     
    330354        minlon = 0;
    331355        maxlon = 0;
    332356        try {
    333             minlat = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("bottom") : ""));
    334             maxlat = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("top") : ""));
    335             minlon = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("left") : ""));
    336             maxlon = LatLon.roundToOsmPrecision(Double.parseDouble(args != null ? args.get("right") : ""));
     357            minlat = LatLon.roundToOsmPrecision(bottomParameter.read(args));
     358            maxlat = LatLon.roundToOsmPrecision(topParameter.read(args));
     359            minlon = LatLon.roundToOsmPrecision(leftParameter.read(args));
     360            maxlon = LatLon.roundToOsmPrecision(rightParameter.read(args));
    337361        } catch (NumberFormatException e) {
    338362            throw new RequestHandlerBadRequestException("NumberFormatException ("+e.getMessage()+')', e);
    339363        }
     
    352376        }
    353377
    354378        // Process optional argument 'select'
    355         if (args != null && args.containsKey("select")) {
     379        if (args != null && selectParameter.isPresent(args)) {
    356380            toSelect.clear();
    357             for (String item : args.get("select").split(",", -1)) {
     381            for (String item : selectParameter.read(args).split(",", -1)) {
    358382                if (!item.isEmpty()) {
    359383                    if (CURRENT_SELECTION.equalsIgnoreCase(item)) {
    360384                        isKeepingCurrentSelection = true;
  • src/org/openstreetmap/josm/io/remotecontrol/handler/LoadDataHandler.java

     
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.io.remotecontrol.handler;
    33
    4 import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import java.io.ByteArrayInputStream;
    7 import java.nio.charset.StandardCharsets;
    8 
    94import org.openstreetmap.josm.actions.downloadtasks.DownloadOsmTask;
    105import org.openstreetmap.josm.actions.downloadtasks.DownloadParams;
    116import org.openstreetmap.josm.data.osm.DataSet;
     
    138import org.openstreetmap.josm.io.IllegalDataException;
    149import org.openstreetmap.josm.io.OsmReader;
    1510import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     11import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    1612import org.openstreetmap.josm.tools.Utils;
    1713
     14import java.io.ByteArrayInputStream;
     15import java.nio.charset.StandardCharsets;
     16import java.util.Arrays;
     17import java.util.Collections;
     18import java.util.List;
     19
     20import static org.openstreetmap.josm.tools.I18n.tr;
     21
    1822/**
    1923 * Handler to load data directly from the URL.
    2024 * @since 7636
     
    2832     */
    2933    public static final String command = "load_data";
    3034
     35    private final RequestParameter<String> dataParameter = RequestParameter.mandatory("data");
     36    private final RequestParameter<String> mimeTypeParameter = RequestParameter.optional("mime_type", () -> OSM_MIME_TYPE);
     37
    3138    /**
    3239     * Holds the data input string
    3340     */
     
    4047
    4148    @Override
    4249    protected void handleRequest() throws RequestHandlerErrorException {
    43         MainApplication.worker.submit(new LoadDataTask(getDownloadParams(), dataSet, args.get("layer_name")));
     50        MainApplication.worker.submit(new LoadDataTask(getDownloadParams(), dataSet, layerNameParameter.read(args)));
    4451    }
    4552
    4653    @Override
    47     public String[] getMandatoryParams() {
    48         return new String[]{"data"};
     54    public List<RequestParameter<?>> getParameters() {
     55        return Arrays.asList(
     56            dataParameter,
     57            mimeTypeParameter,
     58            layerNameParameter,
     59            layerLockedParameter,
     60            downloadPolicyParameter,
     61            uploadPolicyParameter
     62        );
    4963    }
    5064
    5165    @Override
    52     public String[] getOptionalParams() {
    53         return new String[] {"new_layer", "mime_type", "layer_name", "layer_locked", "download_policy", "upload_policy"};
    54     }
    55 
    56     @Override
    5766    public String getUsage() {
    5867        return "Reads data encoded directly in the URL and adds it to the current data set";
    5968    }
     
    6069
    6170    @Override
    6271    public String[] getUsageExamples() {
    63         return new String[]{
    64                 "/load_data?layer_name=extra_layer&new_layer=true&data=" +
    65                     Utils.encodeUrl("<osm version='0.6'><node id='-1' lat='1' lon='2' /></osm>")};
     72        return new String[] {
     73            "/load_data?layer_name=extra_layer&new_layer=true&data=" +
     74                Utils.encodeUrl("<osm version='0.6'><node id='-1' lat='1' lon='2' /></osm>")
     75        };
    6676    }
    6777
    6878    @Override
     
    8090    @Override
    8191    protected void validateRequest() throws RequestHandlerBadRequestException {
    8292        validateDownloadParams();
    83         this.data = args.get("data");
     93        this.data = dataParameter.read(args);
    8494        /**
    8595         * Holds the mime type. Currently only OSM_MIME_TYPE is supported
    8696         * But it could be extended to text/csv, application/gpx+xml, ... or even binary encoded data
    8797         */
    88         final String mimeType = Utils.firstNonNull(args.get("mime_type"), OSM_MIME_TYPE);
     98        final String mimeType = mimeTypeParameter.read(args);
    8999        try {
    90100            if (OSM_MIME_TYPE.equals(mimeType)) {
    91101                final ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
  • src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java

     
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.util.Arrays;
    67import java.util.LinkedList;
    78import java.util.List;
    89import java.util.concurrent.ExecutionException;
     
    1819import org.openstreetmap.josm.gui.util.GuiHelper;
    1920import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
    2021import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     22import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    2123import org.openstreetmap.josm.tools.Logging;
    2224
    2325/**
     
    3436
    3537    private final List<PrimitiveId> ps = new LinkedList<>();
    3638
    37     @Override
    38     public String[] getMandatoryParams() {
    39         return new String[]{"objects"};
    40     }
     39    private final RequestParameter<String> objectsParameter = RequestParameter.mandatory("objects");
     40    private final RequestParameter<Boolean> relationMembersParameter = RequestParameter.optional("relation_members", Boolean::parseBoolean, () -> false, "boolean");
     41    private final RequestParameter<Boolean> referrersParameter = RequestParameter.optional("referrers", Boolean::parseBoolean, () -> false, "boolean");
    4142
    4243    @Override
    43     public String[] getOptionalParams() {
    44         return new String[] {"new_layer", "layer_name", "layer_locked", "download_policy", "upload_policy",
    45                 "addtags", "relation_members", "referrers"};
     44    public List<RequestParameter<?>> getParameters() {
     45        return Arrays.asList(
     46            objectsParameter,
     47            layerNameParameter,
     48            layerLockedParameter,
     49            downloadPolicyParameter,
     50            uploadPolicyParameter,
     51            addTagsParameter,
     52            relationMembersParameter,
     53            referrersParameter
     54        );
    4655    }
    4756
    4857    @Override
    4958    public String getUsage() {
    50         return "downloads the specified objects from the server";
     59        return "Downloads the specified objects from the server";
    5160    }
    5261
    5362    @Override
    5463    public String[] getUsageExamples() {
    55         return new String[] {"/load_object?new_layer=true&objects=w106159509",
     64        return new String[]{
     65            "/load_object?new_layer=true&objects=w106159509",
    5666            "/load_object?new_layer=true&objects=r2263653&relation_members=true",
    5767            "/load_object?objects=n100000&referrers=false"
    5868        };
     
    6474            Logging.info("RemoteControl: download forbidden by preferences");
    6575        }
    6676        if (!ps.isEmpty()) {
    67             final boolean newLayer = getDownloadParams().isNewLayer();
    68             final boolean relationMembers = Boolean.parseBoolean(args.get("relation_members"));
    69             final boolean referrers = Boolean.parseBoolean(args.get("referrers"));
     77            final boolean newLayer = newLayerParameter.read(args);
     78            final boolean relationMembers = relationMembersParameter.read(args);
     79            final boolean referrers = referrersParameter.read(args);
    7080            final DownloadPrimitivesWithReferrersTask task = new DownloadPrimitivesWithReferrersTask(
    71                     newLayer, ps, referrers, relationMembers, args.get("layer_name"), null);
     81                    newLayer, ps, referrers, relationMembers, layerNameParameter.read(args), null);
    7282            try {
    7383                MainApplication.worker.submit(task).get(OSM_DOWNLOAD_TIMEOUT.get(), TimeUnit.SECONDS);
    7484            } catch (InterruptedException | ExecutionException | TimeoutException e) {
     
    100110    protected void validateRequest() throws RequestHandlerBadRequestException {
    101111        validateDownloadParams();
    102112        ps.clear();
    103         for (String i : splitArg("objects", SPLITTER_COMMA)) {
     113        for (String i : SPLITTER_COMMA.split(objectsParameter.read(args), -1)) {
    104114            if (!i.isEmpty()) {
    105115                try {
    106116                    ps.add(SimplePrimitiveId.fromString(i));
  • src/org/openstreetmap/josm/io/remotecontrol/handler/OpenApiHandler.java

     
    66
    77import java.io.StringWriter;
    88import java.util.Arrays;
    9 import java.util.stream.Stream;
     9import java.util.Collections;
     10import java.util.List;
    1011
    1112import javax.json.Json;
    1213import javax.json.JsonArrayBuilder;
     
    1516import org.openstreetmap.josm.data.preferences.JosmUrls;
    1617import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
    1718import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
     19import org.openstreetmap.josm.io.remotecontrol.RemoteControlHttpServer;
    1820import org.openstreetmap.josm.io.remotecontrol.RequestProcessor;
     21import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    1922import org.openstreetmap.josm.tools.Utils;
    2023
    2124/**
     
    3942
    4043    private JsonObjectBuilder getOpenApi() {
    4144        return Json.createObjectBuilder()
    42                 .add("openapi", "3.0.0")
     45                .add("openapi", "3.0.3")
    4346                .add("info", Json.createObjectBuilder()
    4447                        .add("title", RequestProcessor.JOSM_REMOTE_CONTROL)
    4548                        .add("version", RemoteControl.getVersion())
     
    4750                                .add("name", "JOSM")
    4851                                .add("url", JosmUrls.getInstance().getJOSMWebsite())))
    4952                .add("servers", Json.createArrayBuilder()
    50                         .add(Json.createObjectBuilder().add("url", "http://localhost:8111/")))
     53                        .add(Json.createObjectBuilder().add("url", String.format("http://localhost:%s/", RemoteControlHttpServer.PORT.get()))))
    5154                .add("paths", getHandlers());
    5255    }
    5356
     
    6063
    6164    private JsonObjectBuilder getHandler(RequestHandler handler) {
    6265        JsonArrayBuilder parameters = Json.createArrayBuilder();
    63         Stream.concat(
    64                 Arrays.stream(handler.getMandatoryParams()),
    65                 Arrays.stream(handler.getOptionalParams())
    66         ).distinct().map(param -> Json.createObjectBuilder()
    67                 .add("name", param)
    68                 .add("in", "query")
    69                 .add("required", Arrays.asList(handler.getMandatoryParams()).contains(param))
    70                 .add("schema", Json.createObjectBuilder().add("type", "string")) // TODO fix type
    71         ).forEach(parameters::add);
     66        handler.getParameters()
     67            .stream()
     68            .map(param -> Json.createObjectBuilder()
     69                    .add("name", param.getName())
     70                    .add("in", "query")
     71                    .add("required", param.isMandatory())
     72                    .add("schema", Json.createObjectBuilder().add("type", param.getOpenapiType()))
     73            )
     74            .forEach(parameters::add);
    7275        return Json.createObjectBuilder().add("get", Json.createObjectBuilder()
    7376                .add("description", getDescription(handler))
    7477                .add("operationId", handler.getCommand())
    7578                .add("parameters", parameters)
    7679                .add("responses", Json.createObjectBuilder()
    77                         .add("200", Json.createObjectBuilder().add("description", "successful operation")))
     80                        .add("200", Json.createObjectBuilder().add("description", "Successful operation"))
     81                        .add("400", Json.createObjectBuilder().add("description", "Missing required parameters or bad request format"))
     82                        .add("403", Json.createObjectBuilder().add("description", "Action is not permitted"))
     83                        .add("500", Json.createObjectBuilder().add("description", "Internal server error"))
     84                        .add("502", Json.createObjectBuilder().add("description", "Bad gateway (upstream)"))
     85                )
    7886        );
    7987    }
    8088
     
    9098
    9199    @Override
    92100    public String[] getUsageExamples() {
    93         return new String[]{"https://petstore.swagger.io/?url=http://localhost:8111/openapi.json", "https://swagger.io/specification/"};
     101        return new String[]{
     102            "https://petstore.swagger.io/?url=http://localhost:8111/openapi.json",
     103            "https://swagger.io/specification/"
     104        };
    94105    }
    95106
    96107    @Override
     
    104115    }
    105116
    106117    @Override
    107     public String[] getMandatoryParams() {
    108         return new String[0];
     118    public List<RequestParameter<?>> getParameters() {
     119        return Collections.emptyList();
    109120    }
    110121
    111122    @Override
  • src/org/openstreetmap/josm/io/remotecontrol/handler/OpenFileHandler.java

     
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    66import java.io.File;
    7 import java.util.Arrays;
     7import java.util.Collections;
    88import java.util.EnumSet;
     9import java.util.List;
    910
    1011import org.openstreetmap.josm.actions.OpenFileAction;
    1112import org.openstreetmap.josm.gui.io.importexport.Options;
    1213import org.openstreetmap.josm.gui.util.GuiHelper;
    1314import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     15import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    1416
    1517/**
    1618 * Opens a local file
     
    2224     */
    2325    public static final String command = "open_file";
    2426
     27    private final RequestParameter<File> filenameParameter = RequestParameter.mandatory("filename", File::new, "string");
     28
    2529    @Override
    26     public String[] getMandatoryParams() {
    27         return new String[]{"filename"};
     30    public List<RequestParameter<?>> getParameters() {
     31        return Collections.singletonList(filenameParameter);
    2832    }
    2933
    3034    @Override
    3135    public String getUsage() {
    32         return "opens a local file in JOSM";
     36        return "Opens a local file in JOSM";
    3337    }
    3438
    3539    @Override
    3640    public String[] getUsageExamples() {
    37         return new String[] {"/open_file?filename=/tmp/test.osm"};
     41        return new String[]{
     42            "/open_file?filename=/tmp/test.osm"
     43        };
    3844    }
    3945
    4046    @Override
     
    4955            options.add(Options.ALLOW_WEB_RESOURCES);
    5056        }
    5157        GuiHelper.runInEDT(() ->
    52             OpenFileAction.openFiles(Arrays.asList(new File(args.get("filename"))), options.toArray(new Options[0])));
     58            OpenFileAction.openFiles(Collections.singletonList(filenameParameter.read(args)), options.toArray(new Options[0])));
    5359    }
    5460
    5561    @Override
  • src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java

     
    66import java.net.URI;
    77import java.net.URISyntaxException;
    88import java.text.MessageFormat;
     9import java.util.Arrays;
    910import java.util.Collections;
    1011import java.util.HashMap;
    1112import java.util.HashSet;
     
    1617import java.util.function.Function;
    1718import java.util.function.Supplier;
    1819import java.util.regex.Pattern;
     20import java.util.stream.Collectors;
     21import java.util.stream.Stream;
    1922
    2023import javax.swing.JLabel;
    2124import javax.swing.JOptionPane;
     
    2831import org.openstreetmap.josm.gui.MainApplication;
    2932import org.openstreetmap.josm.io.OsmApiException;
    3033import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
     34import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    3135import org.openstreetmap.josm.spi.preferences.Config;
    3236import org.openstreetmap.josm.tools.Logging;
    3337import org.openstreetmap.josm.tools.Pair;
     
    5357    /** past confirmations */
    5458    protected static final PermissionCache PERMISSIONS = new PermissionCache();
    5559
     60    protected final RequestParameter<String> layerNameParameter = RequestParameter.optional("layer_name");
     61    protected final RequestParameter<Boolean> newLayerParameter = RequestParameter.optional("new_layer", Boolean::parseBoolean, LOAD_IN_NEW_LAYER::get, "boolean");
     62    protected final RequestParameter<Boolean> layerLockedParameter = RequestParameter.optional("layer_locked", Boolean::parseBoolean, () -> false, "boolean");
     63    protected final RequestParameter<DownloadPolicy> downloadPolicyParameter = RequestParameter.optional("download_policy", DownloadPolicy::of, () -> DownloadPolicy.NORMAL, "string");
     64    protected final RequestParameter<UploadPolicy> uploadPolicyParameter = RequestParameter.optional("upload_policy", UploadPolicy::of, () -> UploadPolicy.NORMAL, "string");
     65    protected final RequestParameter<String> addTagsParameter = RequestParameter.optional("addtags");
     66
    5667    /** The GET request arguments */
    5768    protected Map<String, String> args;
    5869
     
    129140    public abstract PermissionPrefWithDefault getPermissionPref();
    130141
    131142    /**
     143     * Returns the request parameters. Both used in runtime and for documentation.
     144     *
     145     * @return the request parameters
     146     */
     147    public List<RequestParameter<?>> getParameters() {
     148        // Default implementation for backwards compatibility
     149        return Stream.concat(
     150            Arrays.stream(getMandatoryParams()).map(RequestParameter::mandatory),
     151            Arrays.stream(getOptionalParams()).map(RequestParameter::optional)
     152        ).collect(Collectors.toList());
     153    }
     154
     155    /**
    132156     * Returns the mandatory parameters. Both used to enforce their presence at runtime and for documentation.
     157     * @deprecated implement `getParameters` instead.
    133158     * @return the mandatory parameters
    134159     */
    135     public abstract String[] getMandatoryParams();
     160    @Deprecated
     161    public String[] getMandatoryParams() {
     162        return new String[0];
     163    }
    136164
    137165    /**
    138166     * Returns the optional parameters. Both used to enforce their presence at runtime and for documentation.
     167     * @deprecated implement `getParameters` instead.
    139168     * @return the optional parameters
    140169     */
     170    @Deprecated
    141171    public String[] getOptionalParams() {
    142172        return new String[0];
    143173    }
     
    247277        this.args = getRequestParameter(new URI(this.request));
    248278    }
    249279
    250     protected final String[] splitArg(String arg, Pattern splitter) {
    251         return splitter.split(args != null ? args.get(arg) : "", -1);
    252     }
    253 
    254280    /**
    255281     * Returns the request parameters.
    256282     * @param uri URI as string
     
    271297    }
    272298
    273299    void checkMandatoryParams() throws RequestHandlerBadRequestException {
    274         String[] mandatory = getMandatoryParams();
    275         String[] optional = getOptionalParams();
     300        List<RequestParameter<?>> mandatory = getParameters().stream().filter(RequestParameter::isMandatory).collect(Collectors.toList());
    276301        List<String> missingKeys = new LinkedList<>();
    277302        boolean error = false;
    278         if (mandatory != null && args != null) {
    279             for (String key : mandatory) {
    280                 String value = args.get(key);
    281                 if (Utils.isEmpty(value)) {
     303        if (args != null) {
     304            for (RequestParameter<?> parameter : mandatory) {
     305                String value = args.get(parameter.getName());
     306                if (Utils.isStripEmpty(value)) {
    282307                    error = true;
    283                     Logging.warn('\'' + myCommand + "' remote control request must have '" + key + "' parameter");
    284                     missingKeys.add(key);
     308                    Logging.warn('\'' + myCommand + "' remote control request must have '" + parameter.getName() + "' parameter");
     309                    missingKeys.add(parameter.getName());
    285310                }
    286311            }
    287312        }
    288         Set<String> knownParams = new HashSet<>();
    289         if (mandatory != null)
    290             Collections.addAll(knownParams, mandatory);
    291         if (optional != null)
    292             Collections.addAll(knownParams, optional);
     313        Set<String> knownParams = getParameters().stream().map(RequestParameter::getName).collect(Collectors.toSet());
    293314        if (args != null) {
    294315            for (String par: args.keySet()) {
    295316                if (!knownParams.contains(par)) {
     
    340361        return contentType;
    341362    }
    342363
    343     private <T> T get(String key, Function<String, T> parser, Supplier<T> defaultSupplier) {
    344         String val = args.get(key);
    345         return !Utils.isEmpty(val) ? parser.apply(val) : defaultSupplier.get();
    346     }
    347 
    348     private boolean get(String key) {
    349         return get(key, Boolean::parseBoolean, () -> Boolean.FALSE);
    350     }
    351 
    352     private boolean isLoadInNewLayer() {
    353         return get("new_layer", Boolean::parseBoolean, LOAD_IN_NEW_LAYER::get);
    354     }
    355 
    356364    protected DownloadParams getDownloadParams() {
    357365        DownloadParams result = new DownloadParams();
    358366        if (args != null) {
    359367            result = result
    360                 .withNewLayer(isLoadInNewLayer())
    361                 .withLayerName(args.get("layer_name"))
    362                 .withLocked(get("layer_locked"))
    363                 .withDownloadPolicy(get("download_policy", DownloadPolicy::of, () -> DownloadPolicy.NORMAL))
    364                 .withUploadPolicy(get("upload_policy", UploadPolicy::of, () -> UploadPolicy.NORMAL));
     368                .withNewLayer(newLayerParameter.read(args))
     369                .withLayerName(layerNameParameter.read(args))
     370                .withLocked(layerLockedParameter.read(args))
     371                .withDownloadPolicy(downloadPolicyParameter.read(args))
     372                .withUploadPolicy(uploadPolicyParameter.read(args));
    365373        }
    366374        return result;
    367375    }
     
    501509     */
    502510    public abstract static class RawURLParseRequestHandler extends RequestHandler {
    503511        @Override
    504         protected void parseArgs() throws URISyntaxException {
     512        protected void parseArgs() {
    505513            Map<String, String> args = new HashMap<>();
    506514            if (request.indexOf('?') != -1) {
    507515                String query = request.substring(request.indexOf('?') + 1);
  • src/org/openstreetmap/josm/io/remotecontrol/handler/VersionHandler.java

     
    55
    66import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
    77import org.openstreetmap.josm.io.remotecontrol.RequestProcessor;
     8import org.openstreetmap.josm.io.remotecontrol.parameter.RequestParameter;
    89
     10import java.util.Arrays;
     11import java.util.Collections;
     12import java.util.List;
     13
    914/**
    1015 * Handler for version request.
    1116 */
     
    1621     */
    1722    public static final String command = "version";
    1823
     24    private final RequestParameter<String> jsonpParameter = RequestParameter.optional("jsonp");
     25
    1926    @Override
    2027    protected void handleRequest() throws RequestHandlerErrorException,
    2128            RequestHandlerBadRequestException {
    2229        content = RequestProcessor.PROTOCOLVERSION;
    2330        contentType = "application/json";
    24         if (args.containsKey("jsonp")) {
    25             content = args.get("jsonp") + " && " + args.get("jsonp") + '(' + content + ')';
     31        if (jsonpParameter.isPresent(args)) {
     32            content = jsonpParameter.read(args) + " && " + jsonpParameter.read(args) + '(' + content + ')';
    2633        }
    2734    }
    2835
     
    3744    }
    3845
    3946    @Override
    40     public String[] getMandatoryParams() {
    41         return new String[0];
     47    public List<RequestParameter<?>> getParameters() {
     48        return Collections.singletonList(
     49            jsonpParameter
     50        );
    4251    }
    4352
    4453    @Override
    45     public String[] getOptionalParams() {
    46         return new String[]{"jsonp"};
    47     }
    48 
    49     @Override
    5054    protected void validateRequest() throws RequestHandlerBadRequestException {
    5155        // Nothing to do
    5256    }
     
    5357
    5458    @Override
    5559    public String getUsage() {
    56         return "returns the current protocol version of the installed JOSM RemoteControl";
     60        return "Returns the current protocol version of the installed JOSM RemoteControl";
    5761    }
    5862
    5963    @Override
    6064    public String[] getUsageExamples() {
    61         return new String[] {"/version", "/version?jsonp=test"};
     65        return new String[]{
     66            "/version",
     67            "/version?jsonp=test"
     68        };
    6269    }
    6370}