GroupBackend: Provide a default isOrContainsExternalGroup

Move this code from ChangeQueryBuilder since there's nothing
query-specific about it. This helps ensure future code can avoid the
problem fixed in Change I5cee8556be23 and Change I6526299e8eb9.

Change-Id: Ic5f9df7fcd02856bc58543c074acdd9462aa1d71
Release-Notes: skip
diff --git a/java/com/google/gerrit/server/account/GroupBackend.java b/java/com/google/gerrit/server/account/GroupBackend.java
index 91edaf2..e02c27b 100644
--- a/java/com/google/gerrit/server/account/GroupBackend.java
+++ b/java/com/google/gerrit/server/account/GroupBackend.java
@@ -46,4 +46,28 @@
 
   /** Returns {@code true} if the group with the given UUID is visible to all registered users. */
   boolean isVisibleToAll(AccountGroup.UUID uuid);
+
+  default boolean isOrContainsExternalGroup(AccountGroup.UUID groupId) {
+    if (groupId != null) {
+      GroupDescription.Basic groupDescription = get(groupId);
+      if (!(groupDescription instanceof GroupDescription.Internal)
+          || containsExternalSubGroups((GroupDescription.Internal) groupDescription)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean containsExternalSubGroups(GroupDescription.Internal internalGroup) {
+    for (AccountGroup.UUID subGroupUuid : internalGroup.getSubgroups()) {
+      GroupDescription.Basic subGroupDescription = get(subGroupUuid);
+      if (!(subGroupDescription instanceof GroupDescription.Internal)) {
+        return true;
+      }
+      if (containsExternalSubGroups((GroupDescription.Internal) subGroupDescription)) {
+        return true;
+      }
+    }
+    return false;
+  }
 }
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index c090aa0..c70a948 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -34,7 +34,6 @@
 import com.google.gerrit.entities.Address;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.GroupDescription;
 import com.google.gerrit.entities.GroupReference;
 import com.google.gerrit.entities.PatchSet;
 import com.google.gerrit.entities.Project;
@@ -1241,7 +1240,7 @@
   public Predicate<ChangeData> ownerin(String group) throws QueryParseException, IOException {
     GroupReference g = parseGroup(group);
     AccountGroup.UUID groupId = g.getUUID();
-    if (isOrContainsExternalGroup(args.groupBackend, groupId)) {
+    if (args.groupBackend.isOrContainsExternalGroup(groupId)) {
       return new OwnerinPredicate(args.userFactory, groupId);
     }
 
@@ -1261,7 +1260,7 @@
 
     GroupReference g = parseGroup(group);
     AccountGroup.UUID groupId = g.getUUID();
-    if (isOrContainsExternalGroup(args.groupBackend, groupId)) {
+    if (args.groupBackend.isOrContainsExternalGroup(groupId)) {
       return new UploaderinPredicate(args.userFactory, groupId);
     }
 
@@ -1632,34 +1631,6 @@
     return Predicate.and(predicates);
   }
 
-  protected static boolean isOrContainsExternalGroup(
-      GroupBackend groupBackend, AccountGroup.UUID groupId) {
-    if (groupId != null) {
-      GroupDescription.Basic groupDescription = groupBackend.get(groupId);
-      if (!(groupDescription instanceof GroupDescription.Internal)
-          || containsExternalSubGroups(
-              groupBackend, (GroupDescription.Internal) groupDescription)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  protected static boolean containsExternalSubGroups(
-      GroupBackend groupBackend, GroupDescription.Internal internalGroup) {
-    for (AccountGroup.UUID subGroupUuid : internalGroup.getSubgroups()) {
-      GroupDescription.Basic subGroupDescription = groupBackend.get(subGroupUuid);
-      if (!(subGroupDescription instanceof GroupDescription.Internal)) {
-        return true;
-      }
-      if (containsExternalSubGroups(
-          groupBackend, (GroupDescription.Internal) subGroupDescription)) {
-        return true;
-      }
-    }
-    return false;
-  }
-
   private Set<Account.Id> getMembers(AccountGroup.UUID g) throws IOException {
     Set<Account.Id> accounts;
     Set<Account.Id> allMembers =
diff --git a/java/com/google/gerrit/server/query/change/LabelPredicate.java b/java/com/google/gerrit/server/query/change/LabelPredicate.java
index f50cd04..15356f9 100644
--- a/java/com/google/gerrit/server/query/change/LabelPredicate.java
+++ b/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -158,7 +158,7 @@
   }
 
   protected static Predicate<ChangeData> equalsLabelPredicate(Args args, String label, int expVal) {
-    if (ChangeQueryBuilder.isOrContainsExternalGroup(args.groupBackend, args.group)) {
+    if (args.groupBackend.isOrContainsExternalGroup(args.group)) {
       // We can only get members of internal groups and negating an index search that doesn't
       // include the external group information leads to incorrect query results. Use a
       // PostFilterPredicate in this case instead.
@@ -175,7 +175,7 @@
   }
 
   protected static Predicate<ChangeData> magicLabelPredicate(Args args, MagicLabelVote mlv) {
-    if (ChangeQueryBuilder.isOrContainsExternalGroup(args.groupBackend, args.group)) {
+    if (args.groupBackend.isOrContainsExternalGroup(args.group)) {
       // We can only get members of internal groups and negating an index search that doesn't
       // include the external group information leads to incorrect query results. Use a
       // PostFilterPredicate in this case instead.