ListProjects: Enforce only one of m/p/r is passed

The previous behavior was to silently ignore some arguments; instead,
complain.

Change-Id: If710f1d483a3d42e757fdb1d1b05680817277245
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
index 13ffca9..bb3d2e7 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/project/ListProjectsIT.java
@@ -121,6 +121,11 @@
     Project.NameKey projectAwesome = new Project.NameKey("project-awesome");
     createProject(sshSession, projectAwesome.get());
 
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?p=some&r=.*").getStatusCode());
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?p=some&m=some").getStatusCode());
+
     RestResponse r = GET("/projects/?p=some");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     Map<String, ProjectInfo> result = toProjectInfoMap(r);
@@ -138,14 +143,18 @@
     Project.NameKey projectAwesome = new Project.NameKey("project-awesome");
     createProject(sshSession, projectAwesome.get());
 
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?r=[.*some").getStatusCode());
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?r=.*&p=s").getStatusCode());
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?r=.*&m=s").getStatusCode());
+
     RestResponse r = GET("/projects/?r=.*some");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     Map<String, ProjectInfo> result = toProjectInfoMap(r);
     assertProjects(Arrays.asList(projectAwesome), result.values());
 
-    r = GET("/projects/?r=[.*some");
-    assertEquals(HttpStatus.SC_BAD_REQUEST, r.getStatusCode());
-
     r = GET("/projects/?r=some-project$");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     result = toProjectInfoMap(r);
@@ -186,6 +195,11 @@
     Project.NameKey projectAwesome = new Project.NameKey("project-awesome");
     createProject(sshSession, projectAwesome.get());
 
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?m=some&r=.*").getStatusCode());
+    assertEquals(HttpStatus.SC_BAD_REQUEST,
+        GET("/projects/?m=some&p=some").getStatusCode());
+
     RestResponse r = GET("/projects/?m=some");
     assertEquals(HttpStatus.SC_OK, r.getStatusCode());
     Map<String, ProjectInfo> result = toProjectInfoMap(r);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
index 3c66579..4bddc79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ListProjects.java
@@ -449,8 +449,10 @@
 
   private Iterable<Project.NameKey> scan() throws BadRequestException {
     if (matchPrefix != null) {
+      checkMatchOptions(matchSubstring == null && matchRegex == null);
       return projectCache.byName(matchPrefix);
     } else if (matchSubstring != null) {
+      checkMatchOptions(matchPrefix == null && matchRegex == null);
       return Iterables.filter(projectCache.all(),
           new Predicate<Project.NameKey>() {
             public boolean apply(Project.NameKey in) {
@@ -459,6 +461,7 @@
             }
           });
     } else if (matchRegex != null) {
+      checkMatchOptions(matchPrefix == null && matchSubstring == null);
       if (matchRegex.startsWith("^")) {
         matchRegex = matchRegex.substring(1);
       }
@@ -485,6 +488,13 @@
     }
   }
 
+  private static void checkMatchOptions(boolean cond)
+      throws BadRequestException {
+    if (!cond) {
+      throw new BadRequestException("specify exactly one of p/m/r");
+    }
+  }
+
   private void printProjectTree(final PrintWriter stdout,
       final TreeMap<Project.NameKey, ProjectNode> treeMap) {
     final SortedSet<ProjectNode> sortedNodes = new TreeSet<>();