Refactor UiCommandDetail creation to be a utility class

Necessary to reuse the block of code that creates descriptions,
so that other places can be supported within the server.

Change-Id: I6c89b72a4a6b3c72ce7fc19aa9580a3a99994294
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java
index 788a354..9f4da74 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetDetail.java
@@ -72,6 +72,6 @@
   }
 
   public void setCommands(List<UiCommandDetail> cmds) {
-    commands = cmds;
+    commands = cmds.isEmpty() ? null : cmds;
   }
 }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
index 152673d..868b625 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetDetailFactory.java
@@ -14,12 +14,8 @@
 
 package com.google.gerrit.httpd.rpc.changedetail;
 
-import com.google.common.collect.Lists;
 import com.google.gerrit.common.data.PatchSetDetail;
-import com.google.gerrit.common.data.UiCommandDetail;
 import com.google.gerrit.common.errors.NoSuchEntityException;
-import com.google.gerrit.extensions.registration.DynamicMap;
-import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.extensions.webui.UiCommand;
 import com.google.gerrit.httpd.rpc.Handler;
 import com.google.gerrit.reviewdb.client.Account;
@@ -36,6 +32,7 @@
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.change.Revisions;
+import com.google.gerrit.server.extensions.webui.UiCommands;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListCache;
 import com.google.gerrit.server.patch.PatchListKey;
@@ -52,8 +49,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -177,57 +173,13 @@
       }
     }
 
-    RevisionResource rev =
-        new RevisionResource(new ChangeResource(control), patchSet);
-    detail.setCommands(buildCommands(rev));
+    detail.setCommands(UiCommands.sorted(UiCommands.from(
+      revisions,
+      new RevisionResource(new ChangeResource(control), patchSet),
+      EnumSet.of(UiCommand.Place.PATCHSET_ACTION_PANEL))));
     return detail;
   }
 
-  private List<UiCommandDetail> buildCommands(RevisionResource rev) {
-    List<UiCommandDetail> all = Lists.newArrayList();
-    for (DynamicMap.Entry<RestView<RevisionResource>> e : revisions.views()) {
-      int d = e.getExportName().indexOf('.');
-      if (d < 0) {
-        continue;
-      }
-      String method = e.getExportName().substring(0, d);
-      String name = e.getExportName().substring(d + 1);
-      RestView<RevisionResource> view;
-      try {
-        view = e.getProvider().get();
-      } catch (RuntimeException err) {
-        log.error(String.format(
-            "error in view %s.%s",
-            e.getPluginName(), e.getExportName()), err);
-        continue;
-      }
-      if (!(view instanceof UiCommand)) {
-        continue;
-      }
-
-      UiCommand<RevisionResource> cmd = (UiCommand<RevisionResource>) view;
-      if (cmd.getPlace() != UiCommand.Place.PATCHSET_ACTION_PANEL
-          || !cmd.isVisible(rev)) {
-        continue;
-      }
-
-      UiCommandDetail dsc = new UiCommandDetail();
-      dsc.id = e.getPluginName() + '~' + name;
-      dsc.method = method;
-      dsc.label = cmd.getLabel(rev);
-      dsc.title = cmd.getTitle(rev);
-      dsc.enabled = cmd.isEnabled(rev);
-      all.add(dsc);
-    }
-    Collections.sort(all, new Comparator<UiCommandDetail>() {
-      @Override
-      public int compare(UiCommandDetail a, UiCommandDetail b) {
-        return a.id.compareTo(b.id);
-      }
-    });
-    return all.isEmpty() ? null : all;
-  }
-
   private ObjectId toObjectId(final PatchSet.Id psId) throws OrmException,
       NoSuchEntityException {
     final PatchSet ps = db.patchSets().get(psId);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiCommands.java b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiCommands.java
new file mode 100644
index 0000000..2d1db64
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/extensions/webui/UiCommands.java
@@ -0,0 +1,120 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.extensions.webui;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.gerrit.common.data.UiCommandDetail;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.ChildCollection;
+import com.google.gerrit.extensions.restapi.RestResource;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.extensions.webui.UiCommand;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+public class UiCommands {
+  private static final Logger log = LoggerFactory.getLogger(UiCommands.class);
+
+  public static Predicate<UiCommandDetail> enabled() {
+    return new Predicate<UiCommandDetail>() {
+      @Override
+      public boolean apply(UiCommandDetail input) {
+        return input.enabled;
+      }
+    };
+  }
+
+  public static List<UiCommandDetail> sorted(Iterable<UiCommandDetail> in) {
+    List<UiCommandDetail> s = Lists.newArrayList(in);
+    Collections.sort(s, new Comparator<UiCommandDetail>() {
+      @Override
+      public int compare(UiCommandDetail a, UiCommandDetail b) {
+        return a.id.compareTo(b.id);
+      }
+    });
+    return s;
+  }
+
+  public static <R extends RestResource> Iterable<UiCommandDetail> from(
+      ChildCollection<?, R> collection,
+      R resource,
+      EnumSet<UiCommand.Place> places) {
+    return from(collection.views(), resource, places);
+  }
+
+  public static <R extends RestResource> Iterable<UiCommandDetail> from(
+      DynamicMap<RestView<R>> views,
+      final R resource,
+      final EnumSet<UiCommand.Place> places) {
+    return Iterables.filter(
+      Iterables.transform(
+        views,
+        new Function<DynamicMap.Entry<RestView<R>>, UiCommandDetail> () {
+          @Override
+          @Nullable
+          public UiCommandDetail apply(DynamicMap.Entry<RestView<R>> e) {
+            int d = e.getExportName().indexOf('.');
+            if (d < 0) {
+              return null;
+            }
+
+            String method = e.getExportName().substring(0, d);
+            String name = e.getExportName().substring(d + 1);
+            RestView<R> view;
+            try {
+              view = e.getProvider().get();
+            } catch (RuntimeException err) {
+              log.error(String.format(
+                  "error creating view %s.%s",
+                  e.getPluginName(), e.getExportName()), err);
+              return null;
+            }
+
+            if (!(view instanceof UiCommand)) {
+              return null;
+            }
+
+            UiCommand<R> cmd = (UiCommand<R>) view;
+            if (!places.contains(cmd.getPlace()) || !cmd.isVisible(resource)) {
+              return null;
+            }
+
+            UiCommandDetail dsc = new UiCommandDetail();
+            dsc.id = e.getPluginName() + '~' + name;
+            dsc.method = method;
+            dsc.label = cmd.getLabel(resource);
+            dsc.title = cmd.getTitle(resource);
+            dsc.enabled = cmd.isEnabled(resource);
+            return dsc;
+          }
+        }),
+      Predicates.notNull());
+  }
+
+  private UiCommands() {
+  }
+}