commit 4f3016815dc9eafe0fe3ed4cfc8063487732783d
Author: Simon Legner <Simon.Legner@gmail.com>
Date:   Thu Dec 26 11:43:43 2013 +0100

    see #9476 - Annotations for remote control implementation

diff --git a/src/org/openstreetmap/josm/io/remotecontrol/RequestMethod.java b/src/org/openstreetmap/josm/io/remotecontrol/RequestMethod.java
new file mode 100644
index 0000000..9c7fee5
--- /dev/null
+++ b/src/org/openstreetmap/josm/io/remotecontrol/RequestMethod.java
@@ -0,0 +1,18 @@
+package org.openstreetmap.josm.io.remotecontrol;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface RequestMethod {
+    String command();
+
+    String contentType() default "text/plain";
+
+    String usage();
+
+    String[] usageExamples() default {};
+}
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/RequestParameter.java b/src/org/openstreetmap/josm/io/remotecontrol/RequestParameter.java
new file mode 100644
index 0000000..44aafbb
--- /dev/null
+++ b/src/org/openstreetmap/josm/io/remotecontrol/RequestParameter.java
@@ -0,0 +1,13 @@
+package org.openstreetmap.josm.io.remotecontrol;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface RequestParameter {
+    String name();
+    boolean optional() default false;
+}
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java b/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java
index 30c3acd..c73fc15 100644
--- a/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java
+++ b/src/org/openstreetmap/josm/io/remotecontrol/RequestProcessor.java
@@ -10,10 +10,13 @@ import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
 import java.net.Socket;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.StringTokenizer;
@@ -209,6 +212,30 @@ public class RequestProcessor extends Thread {
 
             // find a handler for this command
             Class<? extends RequestHandler> handlerClass = handlers.get(command);
+
+            for (final Method m : handlerClass.getDeclaredMethods()) {
+                final RequestMethod requestMethod = m.getAnnotation(RequestMethod.class);
+                if (requestMethod != null) {
+                    final Map<String, String> httpArgs = RequestHandler.parseArgs(url);
+                    final List<String> args = Utils.transform(Arrays.asList(m.getParameters()), new Utils.Function<Parameter, String>() {
+                        @Override
+                        public String apply(Parameter x) {
+                            final RequestParameter requestParameter = x.getAnnotation(RequestParameter.class);
+                            if (requestParameter == null) {
+                                return null;
+                            } else {
+                                return httpArgs.get(requestParameter.name());
+                            }
+                        }
+                    });
+                    final String result = (String) m.invoke(handlerClass.newInstance(), args.toArray());
+                    sendHeader(out, "200 OK", requestMethod.contentType(), false);
+                    out.write(result);
+                    out.flush();
+                    return;
+                }
+            }
+
             if (handlerClass == null) {
                 String usage = getUsageAsHtml();
                 String websiteDoc = HelpUtil.getWikiBaseHelpUrl() +"/Help/Preferences/RemoteControl";
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java b/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java
index 592e672..3cfcab5 100644
--- a/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java
+++ b/src/org/openstreetmap/josm/io/remotecontrol/handler/LoadObjectHandler.java
@@ -13,6 +13,8 @@ import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.remotecontrol.AddTagsDialog;
 import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
+import org.openstreetmap.josm.io.remotecontrol.RequestMethod;
+import org.openstreetmap.josm.io.remotecontrol.RequestParameter;
 
 /**
  * Loads OSM primitives using their ID
@@ -46,6 +48,42 @@ public class LoadObjectHandler extends RequestHandler {
         };
     }
 
+    @RequestMethod(
+            command = "load_object",
+            usage = "Download objects with given id.",
+            usageExamples = {"/load_object?new_layer=true&objects=w106159509",
+                    "/load_object?new_layer=true&objects=r2263653&relation_members=true"
+            })
+    public static void loadObject(@RequestParameter(name = "objects") String objects,
+                              @RequestParameter(name="new_layer", optional = false) String new_layer,
+                              @RequestParameter(name="relation_members", optional = false) String relation_members) {
+        final List<PrimitiveId> ps = new LinkedList<PrimitiveId>();
+        for (String i : objects.split(",\\s*")) {
+            try {
+                ps.add(SimplePrimitiveId.fromString(i));
+            } catch (IllegalArgumentException e) {
+                Main.warn("RemoteControl: invalid selection '" + i + "' ignored");
+            }
+        }
+        if (!ps.isEmpty()) {
+            final boolean newLayer = Boolean.parseBoolean(new_layer);
+            final boolean relationMembers = Boolean.parseBoolean(relation_members);
+            GuiHelper.runInEDTAndWait(new Runnable() {
+                @Override public void run() {
+                    DownloadPrimitiveAction.processItems(newLayer, ps, true, relationMembers);
+                }
+            });
+            GuiHelper.executeByMainWorkerInEDT(new Runnable() {
+                @Override
+                public void run() {
+                    Main.main.getCurrentDataSet().setSelected(ps);
+                    // todo AddTagsDialog.addTags(args, sender);
+                    ps.clear();
+                }
+            });
+        }
+    }
+
     @Override
     protected void handleRequest() throws RequestHandlerErrorException, RequestHandlerBadRequestException {
         if (!PermissionPrefWithDefault.LOAD_DATA.isAllowed()) {
diff --git a/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java b/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java
index cfdffd0..28c1136 100644
--- a/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java
+++ b/src/org/openstreetmap/josm/io/remotecontrol/handler/RequestHandler.java
@@ -190,14 +190,18 @@ public abstract class RequestHandler {
      * Can be overridden by subclass.
      */
     protected void parseArgs() {
+        this.args = parseArgs(this.request);
+    }
+
+    public static Map<String, String> parseArgs(String request) {
         try {
-            String req = URLDecoder.decode(this.request, "UTF-8");
+            String req = URLDecoder.decode(request, "UTF-8");
             HashMap<String, String> args = new HashMap<String, String>();
             if (req.indexOf('?') != -1) {
                 String query = req.substring(req.indexOf('?') + 1);
                 if (query.indexOf('#') != -1) {
-                            query = query.substring(0, query.indexOf('#'));
-                        }
+                    query = query.substring(0, query.indexOf('#'));
+                }
                 String[] params = query.split("&", -1);
                 for (String param : params) {
                     int eq = param.indexOf('=');
@@ -206,7 +210,7 @@ public abstract class RequestHandler {
                     }
                 }
             }
-            this.args = args;
+            return args;
         } catch (UnsupportedEncodingException ex) {
             throw new IllegalStateException(ex);
         }
