Added support for multiple cq parameters.

Change-Id: Ica7effe455cc9320ca0a5cc1c8f730167e78e1d8
diff --git a/core/Changes b/core/Changes
index 6aa58f1..291e020 100644
--- a/core/Changes
+++ b/core/Changes
@@ -4,6 +4,8 @@
 11/12/2019
    - Added errors when requesting VC in caching process (margaretha, 
      resolved #47) 
+12/12/2019
+   - Added support for multiple cq parameters (margaretha, resolved #46)
 
 # version 0.62.2
 13/11/2019
diff --git a/core/src/main/java/de/ids_mannheim/korap/service/BasicService.java b/core/src/main/java/de/ids_mannheim/korap/service/BasicService.java
new file mode 100644
index 0000000..040bdca
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/service/BasicService.java
@@ -0,0 +1,17 @@
+package de.ids_mannheim.korap.service;
+
+import java.util.List;
+
+public class BasicService {
+
+    protected String combineMultipleCorpusQuery (List<String> cqList) {
+        String combinedCorpusQuery = null;
+        if (cqList!=null && cqList.size() > 0) {
+            combinedCorpusQuery = cqList.get(0);
+            for (int i = 1; i < cqList.size(); i++) {
+                combinedCorpusQuery += "&" + cqList.get(i);
+            }
+        }
+        return combinedCorpusQuery;
+    }
+}
diff --git a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
index 46452ca..0d1db18 100644
--- a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
+++ b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
@@ -38,7 +38,7 @@
 import de.ids_mannheim.korap.web.SearchKrill;
 
 @Service
-public class SearchService {
+public class SearchService extends BasicService{
 
     private static final boolean DEBUG = false;
 
@@ -123,7 +123,7 @@
 
     @SuppressWarnings("unchecked")
     public String search (String engine, String username, HttpHeaders headers,
-            String q, String ql, String v, String cq, String fields,
+            String q, String ql, String v, List<String> cqList, String fields,
             String pipes, Integer pageIndex, Integer pageInteger, String ctx,
             Integer pageLength, Boolean cutoff, boolean accessRewriteDisabled)
             throws KustvaktException {
@@ -151,6 +151,7 @@
         
         QuerySerializer serializer = new QuerySerializer();
         serializer.setQuery(q, ql, v);
+        String cq = combineMultipleCorpusQuery(cqList);
         if (cq != null) serializer.setCollection(cq);
 
         List<String> fieldList = convertFieldsToList(fields);
diff --git a/core/src/main/java/de/ids_mannheim/korap/service/StatisticService.java b/core/src/main/java/de/ids_mannheim/korap/service/StatisticService.java
new file mode 100644
index 0000000..694e57b
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/service/StatisticService.java
@@ -0,0 +1,98 @@
+package de.ids_mannheim.korap.service;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.response.Notifications;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+@Service
+public class StatisticService extends BasicService {
+
+    @Autowired
+    private SearchKrill searchKrill;
+    @Autowired
+    private KustvaktConfiguration config;
+
+    public String retrieveStatisticsForCorpusQuery (List<String> cqList,
+            boolean isDeprecated) throws KustvaktException {
+
+        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+        String cq = combineMultipleCorpusQuery(cqList);
+        String json = null;
+        if (cq != null && !cq.isEmpty()) {
+            builder.with(cq);
+            json = builder.toJSON();
+        }
+
+        if (json != null) {
+            checkVC(json);
+        }
+        String stats = searchKrill.getStatistics(json);
+
+        if (isDeprecated) {
+            Notifications n = new Notifications();
+            n.addWarning(StatusCodes.DEPRECATED_PARAMETER,
+                    "Parameter corpusQuery is deprecated in favor of cq.");
+            ObjectNode warning = (ObjectNode) n.toJsonNode();
+            ObjectNode node = (ObjectNode) JsonUtils.readTree(stats);
+            node.setAll(warning);
+            stats = node.toString();
+        }
+        
+        if (stats.contains("-1")) {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND);
+        }
+        return stats;
+    }
+
+    private void checkVC (String json) throws KustvaktException {
+        JsonNode node = JsonUtils.readTree(json);
+        node = node.at("/collection");
+        if (node.has("ref")) {
+            String vcName = node.path("ref").asText();
+            if (vcName.contains("/")) {
+                String[] names = vcName.split("/");
+                if (names.length == 2) {
+                    vcName = names[1];
+                }
+            }
+
+            String vcInCaching = config.getVcInCaching();
+            if (vcName.equals(vcInCaching)) {
+                throw new KustvaktException(
+                        de.ids_mannheim.korap.exceptions.StatusCodes.CACHING_VC,
+                        "VC is currently busy and unaccessible due to "
+                                + "caching process",
+                        node.get("ref").asText());
+            }
+        }
+    }
+
+    public String retrieveStatisticsForKoralQuery (String koralQuery)
+            throws KustvaktException {
+        String stats = null;
+        if (koralQuery != null && !koralQuery.isEmpty()) {
+            checkVC(koralQuery);
+            stats = searchKrill.getStatistics(koralQuery);
+        }
+        else {
+            stats = searchKrill.getStatistics(null);
+        }
+        
+        if (stats.contains("-1")) {
+            throw new KustvaktException(StatusCodes.NO_RESULT_FOUND);
+        }
+        return stats;
+    }
+}
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
index 76bf89d..383def3 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
@@ -2,6 +2,7 @@
                                              // de.ids_mannheim.korap.ext.web;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Set;
 
@@ -184,7 +185,7 @@
             @QueryParam("fields") String fields,
             @QueryParam("pipes") String pipes,
             @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
-            @QueryParam("cq") String cq, 
+            @QueryParam("cq") List<String> cq, 
             @QueryParam("engine") String engine) {
 
         TokenContext context =
@@ -270,7 +271,6 @@
             @QueryParam("fields") String fields,
             @Context SecurityContext ctx,
             @Context HttpHeaders headers
-            // @QueryParam("fields") Set<String> fields
     ) throws KustvaktException {
         TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
         try {
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java b/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
index 51bcaae..9105af3 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/controller/StatisticController.java
@@ -1,5 +1,6 @@
 package de.ids_mannheim.korap.web.controller;
 
+import java.util.List;
 import java.util.Locale;
 
 import javax.ws.rs.Consumes;
@@ -18,18 +19,11 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Controller;
 
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.sun.jersey.spi.container.ResourceFilters;
 
-import de.ids_mannheim.korap.config.KustvaktConfiguration;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
-import de.ids_mannheim.korap.exceptions.StatusCodes;
-import de.ids_mannheim.korap.response.Notifications;
-import de.ids_mannheim.korap.utils.JsonUtils;
-import de.ids_mannheim.korap.utils.KoralCollectionQueryBuilder;
+import de.ids_mannheim.korap.service.StatisticService;
 import de.ids_mannheim.korap.web.CoreResponseHandler;
-import de.ids_mannheim.korap.web.SearchKrill;
 import de.ids_mannheim.korap.web.filter.APIVersionFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
 
@@ -54,10 +48,8 @@
     @Autowired
     private CoreResponseHandler kustvaktResponseHandler;
     @Autowired
-    private SearchKrill searchKrill;
-    @Autowired
-    private KustvaktConfiguration config;
-    
+    private StatisticService service;
+
     /**
      * Returns statistics of the virtual corpus defined by the given
      * corpusQuery parameter.
@@ -69,79 +61,32 @@
      * @param cq
      *            a collection query specifying a virtual corpus
      * @param corpusQuery
-     *            (DEPRECATED) a collection query specifying a virtual corpus 
+     *            (DEPRECATED) a collection query specifying a virtual
+     *            corpus
      * @return statistics of the virtual corpus defined by the given
      *         corpusQuery parameter.
      */
     @GET
     public Response getStatistics (@Context SecurityContext context,
-            @Context Locale locale,
-            @QueryParam("cq") String cq,
-            @QueryParam("corpusQuery") String corpusQuery) {
-
-        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+            @Context Locale locale, @QueryParam("cq") List<String> cq,
+            @QueryParam("corpusQuery") List<String> corpusQuery) {
 
         String stats;
-        String json = null;
         boolean isDeprecated = false;
         try {
-            if (cq != null && !cq.isEmpty()) {
-                builder.with(cq);
-                json = builder.toJSON();
-            }
-            else if (corpusQuery != null && !corpusQuery.isEmpty()) {
-                builder.with(corpusQuery);
-                json = builder.toJSON();
+            if (cq.isEmpty() && corpusQuery != null && !corpusQuery.isEmpty()) {
                 isDeprecated = true;
+                cq = corpusQuery;
             }
-            
-            checkVC(json);
-            stats = searchKrill.getStatistics(json);
-            
-            if (isDeprecated){
-                Notifications n = new Notifications();
-                n.addWarning(StatusCodes.DEPRECATED_PARAMETER,
-                        "Parameter corpusQuery is deprecated in favor of cq.");
-                ObjectNode warning = (ObjectNode) n.toJsonNode();
-                ObjectNode node = (ObjectNode) JsonUtils.readTree(stats);
-                node.setAll(warning);
-                stats = node.toString();
+            stats = service.retrieveStatisticsForCorpusQuery(cq, isDeprecated);
+            if (DEBUG) {
+                jlog.debug("Stats: " + stats);
             }
+            return Response.ok(stats).build();
         }
         catch (KustvaktException e) {
             throw kustvaktResponseHandler.throwit(e);
         }
-        
-        if (stats.contains("-1")) {
-            throw kustvaktResponseHandler.throwit(StatusCodes.NO_RESULT_FOUND);
-        }
-        if (DEBUG) {
-            jlog.debug("Stats: " + stats);
-        }
-        return Response.ok(stats).build();
-    }
-    
-    private void checkVC (String json) throws KustvaktException {
-        JsonNode node = JsonUtils.readTree(json);
-        node = node.at("/collection");
-        if (node.has("ref")){
-            String vcName = node.path("ref").asText();
-            if (vcName.contains("/")) {
-                String[] names = vcName.split("/");
-                if (names.length == 2) {
-                    vcName = names[1];
-                }
-            }
-            
-            String vcInCaching = config.getVcInCaching();
-            if (vcName.equals(vcInCaching)) {
-                throw new KustvaktException(
-                        de.ids_mannheim.korap.exceptions.StatusCodes.CACHING_VC,
-                        "VC is currently busy and unaccessible due to "
-                                + "caching process",
-                        node.get("ref").asText());
-            }
-        }
     }
 
     @POST
@@ -149,20 +94,8 @@
     public Response getStatisticsFromKoralQuery (
             @Context SecurityContext context, @Context Locale locale,
             String koralQuery) {
-        String stats;
         try {
-            if (koralQuery != null && !koralQuery.isEmpty()) {
-                checkVC(koralQuery);
-                stats = searchKrill.getStatistics(koralQuery);
-            }
-            else {
-                stats = searchKrill.getStatistics(null);
-            }
-
-            if (stats.contains("-1")) {
-                throw kustvaktResponseHandler
-                        .throwit(StatusCodes.NO_RESULT_FOUND);
-            }
+            String stats = service.retrieveStatisticsForKoralQuery(koralQuery);
             return Response.ok(stats).build();
         }
         catch (KustvaktException e) {