Merge "Disentangle ListPlugins and PluginLsCommand"
diff --git a/Documentation/rest-api-plugins.txt b/Documentation/rest-api-plugins.txt
index 0f687bf..938d101 100644
--- a/Documentation/rest-api-plugins.txt
+++ b/Documentation/rest-api-plugins.txt
@@ -47,6 +47,7 @@
     "delete-project": {
       "id": "delete-project",
       "index_url": "plugins/delete-project/",
+      "filename": "delete-project.jar",
       "version": "2.9-SNAPSHOT"
     }
   }
@@ -73,11 +74,13 @@
     "delete-project": {
       "id": "delete-project",
       "index_url": "plugins/delete-project/",
+      "filename": "delete-project.jar",
       "version": "2.9-SNAPSHOT"
     },
     "reviewers-by-blame": {
       "id": "reviewers-by-blame",
       "index_url": "plugins/reviewers-by-blame/",
+      "filename": "reviewers-by-blame.jar",
       "version": "2.9-SNAPSHOT",
       "disabled": true
     }
@@ -105,6 +108,7 @@
     "delete-project": {
       "id": "delete-project",
       "index_url": "plugins/delete-project/",
+      "filename": "delete-project.jar",
       "version": "2.9-SNAPSHOT"
     }
   }
@@ -134,6 +138,7 @@
     "delete-project": {
       "id": "delete-project",
       "index_url": "plugins/delete-project/",
+      "filename": "delete-project.jar",
       "version": "2.9-SNAPSHOT"
     }
   }
@@ -168,11 +173,13 @@
     "some-plugin": {
       "id": "some-plugin",
       "index_url": "plugins/some-plugin/",
+      "filename": "some-plugin.jar",
       "version": "2.9-SNAPSHOT"
     },
     "some-other-plugin": {
       "id": "some-other-plugin",
       "index_url": "plugins/some-other-plugin/",
+      "filename": "some-other-plugin.jar",
       "version": "2.9-SNAPSHOT"
     }
   }
@@ -200,6 +207,7 @@
     "reviewers-by-blame": {
       "id": "reviewers-by-blame",
       "index_url": "plugins/reviewers-by-blame/",
+      "filename": "reviewers-by-blame.jar",
       "version": "2.9-SNAPSHOT",
       "disabled": true
     }
@@ -229,6 +237,7 @@
     "delete-project": {
       "id": "delete-project",
       "index_url": "plugins/delete-project/",
+      "filename": "delete-project.jar",
       "version": "2.9-SNAPSHOT"
     }
   }
@@ -429,6 +438,7 @@
 |`id`       ||The ID of the plugin.
 |`version`  ||The version of the plugin.
 |`index_url`|optional|URL of the plugin's default page.
+|`filename` |optional|The plugin's filename.
 |`disabled` |not set if `false`|Whether the plugin is disabled.
 |=======================
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
index 3e1b2cb..0fa09af 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/plugin/PluginIT.java
@@ -68,6 +68,7 @@
       assertThat(info.id).isEqualTo(name);
       assertThat(info.version).isEqualTo(pluginVersion(plugin));
       assertThat(info.indexUrl).isEqualTo(String.format("plugins/%s/", name));
+      assertThat(info.filename).isEqualTo(plugin);
       assertThat(info.disabled).isNull();
     }
     assertPlugins(list().get(), PLUGINS);
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginInfo.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginInfo.java
index bcb957e..0df6235 100644
--- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginInfo.java
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/common/PluginInfo.java
@@ -18,12 +18,14 @@
   public final String id;
   public final String version;
   public final String indexUrl;
+  public final String filename;
   public final Boolean disabled;
 
-  public PluginInfo(String id, String version, String indexUrl, Boolean disabled) {
+  public PluginInfo(String id, String version, String indexUrl, String filename, Boolean disabled) {
     this.id = id;
     this.version = version;
     this.indexUrl = indexUrl;
+    this.filename = filename;
     this.disabled = disabled;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
index 75fb350..a955abe 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/api/plugins/PluginsImpl.java
@@ -66,7 +66,7 @@
         list.setMatchPrefix(this.getPrefix());
         list.setMatchSubstring(this.getSubstring());
         list.setMatchRegex(this.getRegex());
-        return list.apply();
+        return list.apply(TopLevelResource.INSTANCE);
       }
     };
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
index 0e514d6..97d728d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/plugins/ListPlugins.java
@@ -15,11 +15,8 @@
 package com.google.gerrit.server.plugins;
 
 import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.toList;
 
-import com.google.common.base.Strings;
 import com.google.common.collect.Streams;
-import com.google.gerrit.common.Nullable;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
 import com.google.gerrit.extensions.common.PluginInfo;
@@ -27,16 +24,12 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.server.OutputFormat;
-import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
-import java.io.PrintWriter;
-import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.kohsuke.args4j.Option;
 
@@ -52,10 +45,6 @@
   private String matchSubstring;
   private String matchRegex;
 
-  @Deprecated
-  @Option(name = "--format", usage = "(deprecated) output format")
-  private OutputFormat format = OutputFormat.TEXT;
-
   @Option(
     name = "--all",
     aliases = {"-a"},
@@ -115,29 +104,8 @@
     this.pluginLoader = pluginLoader;
   }
 
-  public OutputFormat getFormat() {
-    return format;
-  }
-
-  public ListPlugins setFormat(OutputFormat fmt) {
-    this.format = fmt;
-    return this;
-  }
-
   @Override
-  public Object apply(TopLevelResource resource) throws BadRequestException {
-    format = OutputFormat.JSON;
-    return display(null);
-  }
-
-  public SortedMap<String, PluginInfo> apply() throws BadRequestException {
-    format = OutputFormat.JSON;
-    return display(null);
-  }
-
-  public SortedMap<String, PluginInfo> display(@Nullable PrintWriter stdout)
-      throws BadRequestException {
-    SortedMap<String, PluginInfo> output = new TreeMap<>();
+  public SortedMap<String, PluginInfo> apply(TopLevelResource resource) throws BadRequestException {
     Stream<Plugin> s = Streams.stream(pluginLoader.getPlugins(all));
     if (matchPrefix != null) {
       checkMatchOptions(matchSubstring == null && matchRegex == null);
@@ -158,38 +126,7 @@
     if (limit > 0) {
       s = s.limit(limit);
     }
-    List<Plugin> plugins = s.collect(toList());
-
-    if (!format.isJson()) {
-      stdout.format("%-30s %-10s %-8s %s\n", "Name", "Version", "Status", "File");
-      stdout.print(
-          "-------------------------------------------------------------------------------\n");
-    }
-
-    for (Plugin p : plugins) {
-      PluginInfo info = toPluginInfo(p);
-      if (format.isJson()) {
-        output.put(p.getName(), info);
-      } else {
-        stdout.format(
-            "%-30s %-10s %-8s %s\n",
-            p.getName(),
-            Strings.nullToEmpty(info.version),
-            p.isDisabled() ? "DISABLED" : "ENABLED",
-            p.getSrcFile().getFileName());
-      }
-    }
-
-    if (stdout == null) {
-      return output;
-    } else if (format.isJson()) {
-      format
-          .newGson()
-          .toJson(output, new TypeToken<Map<String, PluginInfo>>() {}.getType(), stdout);
-      stdout.print('\n');
-    }
-    stdout.flush();
-    return null;
+    return new TreeMap<>(s.collect(Collectors.toMap(p -> p.getName(), p -> toPluginInfo(p))));
   }
 
   private void checkMatchOptions(boolean cond) throws BadRequestException {
@@ -202,13 +139,20 @@
     String id;
     String version;
     String indexUrl;
+    String filename;
     Boolean disabled;
 
     id = Url.encode(p.getName());
     version = p.getVersion();
     disabled = p.isDisabled() ? true : null;
-    indexUrl = p.getSrcFile() != null ? String.format("plugins/%s/", p.getName()) : null;
+    if (p.getSrcFile() != null) {
+      indexUrl = String.format("plugins/%s/", p.getName());
+      filename = p.getSrcFile().getFileName().toString();
+    } else {
+      indexUrl = null;
+      filename = null;
+    }
 
-    return new PluginInfo(id, version, indexUrl, disabled);
+    return new PluginInfo(id, version, indexUrl, filename, disabled);
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
index 78c9526..0fdd105 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/PluginLsCommand.java
@@ -16,25 +16,68 @@
 
 import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.common.PluginInfo;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.server.OutputFormat;
 import com.google.gerrit.server.plugins.ListPlugins;
 import com.google.gerrit.sshd.CommandMetaData;
 import com.google.gerrit.sshd.SshCommand;
+import com.google.gson.reflect.TypeToken;
 import com.google.inject.Inject;
+import java.util.Map;
+import org.kohsuke.args4j.Option;
 
 @RequiresCapability(GlobalCapability.VIEW_PLUGINS)
 @CommandMetaData(name = "ls", description = "List the installed plugins", runsAt = MASTER_OR_SLAVE)
 final class PluginLsCommand extends SshCommand {
-  @Inject private ListPlugins impl;
+  @Inject private ListPlugins list;
+
+  @Option(
+    name = "--all",
+    aliases = {"-a"},
+    usage = "List all plugins, including disabled plugins"
+  )
+  private boolean all;
+
+  @Option(name = "--format", usage = "output format")
+  private OutputFormat format = OutputFormat.TEXT;
 
   @Override
   public void run() throws Exception {
-    impl.display(stdout);
+    list.setAll(all);
+    Map<String, PluginInfo> output = list.apply(TopLevelResource.INSTANCE);
+
+    if (format.isJson()) {
+      format
+          .newGson()
+          .toJson(output, new TypeToken<Map<String, PluginInfo>>() {}.getType(), stdout);
+      stdout.print('\n');
+    } else {
+      stdout.format("%-30s %-10s %-8s %s\n", "Name", "Version", "Status", "File");
+      stdout.print(
+          "-------------------------------------------------------------------------------\n");
+      for (Map.Entry<String, PluginInfo> p : output.entrySet()) {
+        PluginInfo info = p.getValue();
+        stdout.format(
+            "%-30s %-10s %-8s %s\n",
+            p.getKey(),
+            Strings.nullToEmpty(info.version),
+            status(info.disabled),
+            Strings.nullToEmpty(info.filename));
+      }
+    }
+    stdout.flush();
+  }
+
+  private String status(Boolean disabled) {
+    return disabled != null && disabled.booleanValue() ? "DISABLED" : "ENABLED";
   }
 
   @Override
   protected void parseCommandLine() throws UnloggedFailure {
-    parseCommandLine(impl);
+    parseCommandLine(this);
   }
 }