Add query reference web-service.

Change-Id: Ic8e8818ea7ade9e1c330ddcb9e9e6851c62adfd1
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
index eaa462e..57d1d9b 100644
--- a/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
+++ b/full/src/main/java/de/ids_mannheim/korap/config/NamedVCLoader.java
@@ -17,7 +17,8 @@
 import org.springframework.stereotype.Component;
 
 import de.ids_mannheim.korap.KrillCollection;
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.QueryType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.entity.VirtualCorpus;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.service.VirtualCorpusService;
@@ -59,8 +60,9 @@
         String json = IOUtils.toString(is, "utf-8");
         if (json != null) {
             cacheVC(json, filename);
-            vcService.storeVC(filename, VirtualCorpusType.SYSTEM, json, null,
-                    null, null, true, "system");
+            vcService.storeVC(filename, ResourceType.SYSTEM,
+                    QueryType.VIRTUAL_CORPUS, json, null, null, null, true,
+                    "system");
         }
     }
 
@@ -100,8 +102,9 @@
                     // ignore
                     if (DEBUG) jlog.debug(e);
                 }
-                vcService.storeVC(filename, VirtualCorpusType.SYSTEM, json,
-                        null, null, null, true, "system");
+                vcService.storeVC(filename, ResourceType.SYSTEM,
+                        QueryType.VIRTUAL_CORPUS, json, null, null, null, true,
+                        "system");
             }
         }
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/QueryType.java b/full/src/main/java/de/ids_mannheim/korap/constant/QueryType.java
new file mode 100644
index 0000000..d223056
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/QueryType.java
@@ -0,0 +1,7 @@
+package de.ids_mannheim.korap.constant;
+
+public enum QueryType {
+
+    QUERY,
+    VIRTUAL_CORPUS;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java b/full/src/main/java/de/ids_mannheim/korap/constant/ResourceType.java
similarity index 95%
rename from full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
rename to full/src/main/java/de/ids_mannheim/korap/constant/ResourceType.java
index 0aea814..27cd9b8 100644
--- a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/ResourceType.java
@@ -12,7 +12,7 @@
  *   This should probably be renamed to something like RessourceType,
  *   as QueryReferences will use the same types.
  */
-public enum VirtualCorpusType {
+public enum ResourceType {
     /**
      * available for all
      */
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java
index 2d849b6..0cb4ae7 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java
@@ -17,7 +17,7 @@
 import de.ids_mannheim.korap.exceptions.KustvaktException;
 import de.ids_mannheim.korap.exceptions.StatusCodes;
 
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.utils.ParameterChecker;
 
 import de.ids_mannheim.korap.entity.QueryReference;
@@ -47,7 +47,7 @@
      */
     public int createQuery(
         String qName,
-        VirtualCorpusType type,
+        ResourceType type,
         // CorpusAccess requiredAccess,
         String koralQuery,
         String definition,
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
index f367571..bbd9f2c 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/VirtualCorpusDao.java
@@ -21,9 +21,10 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
+import de.ids_mannheim.korap.constant.QueryType;
 import de.ids_mannheim.korap.constant.UserGroupStatus;
 import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.entity.UserGroup;
 import de.ids_mannheim.korap.entity.UserGroupMember;
 import de.ids_mannheim.korap.entity.UserGroupMember_;
@@ -52,14 +53,15 @@
     @PersistenceContext
     private EntityManager entityManager;
 
-    public int createVirtualCorpus (String name, VirtualCorpusType type,
-            CorpusAccess requiredAccess, String koralQuery, String definition,
-            String description, String status, boolean isCached,
-            String createdBy) throws KustvaktException {
+    public int createVirtualCorpus (String name, ResourceType type,
+            QueryType queryType, CorpusAccess requiredAccess, String koralQuery,
+            String definition, String description, String status,
+            boolean isCached, String createdBy) throws KustvaktException {
 
         VirtualCorpus vc = new VirtualCorpus();
         vc.setName(name);
         vc.setType(type);
+        vc.setQueryType(queryType);
         vc.setRequiredAccess(requiredAccess);
         vc.setKoralQuery(koralQuery);
         vc.setDefinition(definition);
@@ -73,7 +75,7 @@
     }
 
     public void editVirtualCorpus (VirtualCorpus vc, String name,
-            VirtualCorpusType type, CorpusAccess requiredAccess,
+            ResourceType type, CorpusAccess requiredAccess,
             String koralQuery, String definition, String description,
             String status, boolean isCached) throws KustvaktException {
 
@@ -121,14 +123,14 @@
      * retrieves virtual corpora of all users.
      * 
      * @param type
-     *            {@link VirtualCorpusType}
+     *            {@link ResourceType}
      * @param createdBy
      *            username of the virtual corpus creator
      * @return a list of {@link VirtualCorpus}
      * @throws KustvaktException
      */
     @SuppressWarnings("unchecked")
-    public List<VirtualCorpus> retrieveVCByType (VirtualCorpusType type,
+    public List<VirtualCorpus> retrieveVCByType (ResourceType type,
             String createdBy) throws KustvaktException {
 
         CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
@@ -246,7 +248,7 @@
 
     @SuppressWarnings("unchecked")
     public List<VirtualCorpus> retrieveOwnerVCByType (String userId,
-            VirtualCorpusType type) throws KustvaktException {
+            ResourceType type) throws KustvaktException {
         ParameterChecker.checkStringValue(userId, "userId");
 
         CriteriaBuilder builder = entityManager.getCriteriaBuilder();
@@ -322,7 +324,7 @@
                 builder.equal(virtualCorpus.get(VirtualCorpus_.createdBy),
                         userId),
                 builder.equal(virtualCorpus.get(VirtualCorpus_.type),
-                        VirtualCorpusType.SYSTEM));
+                        ResourceType.SYSTEM));
 
         query.select(virtualCorpus);
         query.where(predicate);
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/VirtualCorpusDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/VirtualCorpusDto.java
index ae530c2..878dcf4 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/VirtualCorpusDto.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/VirtualCorpusDto.java
@@ -1,5 +1,8 @@
 package de.ids_mannheim.korap.dto;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
 import de.ids_mannheim.korap.entity.VirtualCorpus;
 import lombok.Getter;
 import lombok.Setter;
@@ -12,6 +15,7 @@
  */
 @Getter
 @Setter
+@JsonInclude(Include.NON_DEFAULT)
 public class VirtualCorpusDto {
 
     private int id;
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/VirtualCorpusConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/VirtualCorpusConverter.java
index f9872f7..fb16932 100644
--- a/full/src/main/java/de/ids_mannheim/korap/dto/converter/VirtualCorpusConverter.java
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/VirtualCorpusConverter.java
@@ -33,12 +33,13 @@
         dto.setType(vc.getType().displayName());
         dto.setKoralQuery(vc.getKoralQuery());
 
-        JsonNode node = JsonUtils.readTree(statistics);
-        dto.setNumberOfDoc(node.at("/documents").asInt());
-        dto.setNumberOfParagraphs(node.at("/paragraphs").asInt());
-        dto.setNumberOfSentences(node.at("/sentences").asInt());
-        dto.setNumberOfTokens(node.at("/tokens").asInt());
-
+        if (statistics != null) {
+            JsonNode node = JsonUtils.readTree(statistics);
+            dto.setNumberOfDoc(node.at("/documents").asInt());
+            dto.setNumberOfParagraphs(node.at("/paragraphs").asInt());
+            dto.setNumberOfSentences(node.at("/sentences").asInt());
+            dto.setNumberOfTokens(node.at("/tokens").asInt());
+        }
         return dto;
 
     }
diff --git a/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java b/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java
index 2a6ecff..1a9c843 100644
--- a/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java
+++ b/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java
@@ -14,7 +14,7 @@
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -39,7 +39,7 @@
     private int id;
     private String name;
     @Enumerated(EnumType.STRING)
-    private VirtualCorpusType type; // TODO (nd): This should be named RessourceType
+    private ResourceType type;
     private String status;
     private String description;
     // @Enumerated(EnumType.STRING)
diff --git a/full/src/main/java/de/ids_mannheim/korap/entity/VirtualCorpus.java b/full/src/main/java/de/ids_mannheim/korap/entity/VirtualCorpus.java
index 24bdc6d..1f83756 100644
--- a/full/src/main/java/de/ids_mannheim/korap/entity/VirtualCorpus.java
+++ b/full/src/main/java/de/ids_mannheim/korap/entity/VirtualCorpus.java
@@ -14,7 +14,8 @@
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.QueryType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.user.User.CorpusAccess;
 import lombok.Getter;
 import lombok.Setter;
@@ -43,7 +44,10 @@
     private int id;
     private String name;
     @Enumerated(EnumType.STRING)
-    private VirtualCorpusType type;
+    private ResourceType type;
+    @Enumerated(EnumType.STRING)
+    @Column(name = "query_type")
+    private QueryType queryType;
     private String status;
     private String description;
     @Enumerated(EnumType.STRING)
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
index 04d7b67..fe76821 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
@@ -17,7 +17,7 @@
 import de.ids_mannheim.korap.dao.AdminDao;
 import de.ids_mannheim.korap.dao.QueryReferenceDao;
 
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 
 import de.ids_mannheim.korap.entity.QueryReference;
 
@@ -116,7 +116,7 @@
         try {
             qId = qDao.createQuery(
                 qName,
-                VirtualCorpusType.PRIVATE,
+                ResourceType.PRIVATE,
                 qJson,
                 "", // TODO: definition,
                 desc,
@@ -145,7 +145,75 @@
         //   based on its type.
     };
 
+    /*
+    public Status handlePutRequest (String username,
+                                    String qCreator,
+                                    String qName,
+                                    String qJson) throws KustvaktException {
+        
+        verifyUsername(username, qCreator);
+        String q = vcDao.retrieveQueryByName(qName, qCreator);
+        // ParameterChecker.checkObjectValue(qJson, "request entity");
+        ParameterChecker.checkStringValue(qJson, "q");
+        if (q == null) {
+            storeQuery(qJson, qName, username);
+            return Status.CREATED;
+        }
+        else {
+            editQuery(q, qJson, qName, username);
+            return Status.NO_CONTENT;
+        };
+    };
+    */
 
+    /*
+    public void editQuery (QueryRefrence existingQ,
+                           String newQJson,
+                           String qName,
+                           String username) throws KustvaktException {
+
+        if (!username.equals(existingQ.getCreatedBy())
+            && !adminDao.isAdmin(username)) {
+            throw new KustvaktException(
+                StatusCodes.AUTHORIZATION_FAILED,
+                "Unauthorized operation for user: " + username, username);
+        };
+
+        String corpusQuery = newVC.getCorpusQuery();
+
+        if (newQJson != null && !newQJson.isEmpty()) {
+            koralQuery = serializeCorpusQuery(corpusQuery);
+            requiredAccess = determineRequiredAccess(newVC.isCached(), vcName,
+                    koralQuery);
+        };
+
+        VirtualCorpusType type = newVC.getType();
+        if (type != null) {
+            if (existingVC.getType().equals(VirtualCorpusType.PUBLISHED)) {
+                // withdraw from publication
+                if (!type.equals(VirtualCorpusType.PUBLISHED)) {
+                    VirtualCorpusAccess hiddenAccess =
+                            accessDao.retrieveHiddenAccess(existingVC.getId());
+                    deleteVCAccess(hiddenAccess.getId(), "system");
+                    int groupId = hiddenAccess.getUserGroup().getId();
+                    userGroupService.deleteAutoHiddenGroup(groupId, "system");
+                    // EM: should the users within the hidden group receive 
+                    // notifications? 
+                }
+                // else remains the same
+            }
+            else if (type.equals(VirtualCorpusType.PUBLISHED)) {
+                publishVC(existingVC.getId());
+            }
+        }
+
+        vcDao.editVirtualCorpus(existingVC, vcName, type, requiredAccess,
+                koralQuery, newVC.getDefinition(), newVC.getDescription(),
+                newVC.getStatus(), newVC.isCached());
+    }
+    */
+
+    
     /**
      * Only admin and the owner of the virtual corpus are allowed to
      * delete a virtual corpus.
@@ -180,4 +248,53 @@
                 "Unauthorized operation for user: " + username, username);
         };
     };
+
+
+    /*
+    public void editVC (
+        VirtualCorpus existingVC,
+        VirtualCorpusJson newVC,
+        String vcName,
+        String username) throws KustvaktException {
+
+        if (!username.equals(existingVC.getCreatedBy())
+                && !adminDao.isAdmin(username)) {
+            throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+                    "Unauthorized operation for user: " + username, username);
+        }
+
+        String koralQuery = null;
+        CorpusAccess requiredAccess = null;
+        String corpusQuery = newVC.getCorpusQuery();
+        if (corpusQuery != null && !corpusQuery.isEmpty()) {
+            koralQuery = serializeCorpusQuery(corpusQuery);
+            requiredAccess = determineRequiredAccess(newVC.isCached(), vcName,
+                    koralQuery);
+        }
+
+        VirtualCorpusType type = newVC.getType();
+        if (type != null) {
+            if (existingVC.getType().equals(VirtualCorpusType.PUBLISHED)) {
+                // withdraw from publication
+                if (!type.equals(VirtualCorpusType.PUBLISHED)) {
+                    VirtualCorpusAccess hiddenAccess =
+                            accessDao.retrieveHiddenAccess(existingVC.getId());
+                    deleteVCAccess(hiddenAccess.getId(), "system");
+                    int groupId = hiddenAccess.getUserGroup().getId();
+                    userGroupService.deleteAutoHiddenGroup(groupId, "system");
+                    // EM: should the users within the hidden group receive 
+                    // notifications? 
+                }
+                // else remains the same
+            }
+            else if (type.equals(VirtualCorpusType.PUBLISHED)) {
+                publishVC(existingVC.getId());
+            }
+        }
+
+        vcDao.editVirtualCorpus(existingVC, vcName, type, requiredAccess,
+                koralQuery, newVC.getDefinition(), newVC.getDescription(),
+                newVC.getStatus(), newVC.isCached());
+    }
+    */
 };
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index 1163573..9d5ea85 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -20,8 +20,9 @@
 import de.ids_mannheim.korap.KrillCollection;
 import de.ids_mannheim.korap.config.FullConfiguration;
 import de.ids_mannheim.korap.constant.GroupMemberStatus;
+import de.ids_mannheim.korap.constant.QueryType;
 import de.ids_mannheim.korap.constant.VirtualCorpusAccessStatus;
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.dao.AdminDao;
 import de.ids_mannheim.korap.dao.VirtualCorpusAccessDao;
 import de.ids_mannheim.korap.dao.VirtualCorpusDao;
@@ -42,7 +43,7 @@
 import de.ids_mannheim.korap.utils.ParameterChecker;
 import de.ids_mannheim.korap.web.SearchKrill;
 import de.ids_mannheim.korap.web.controller.VirtualCorpusController;
-import de.ids_mannheim.korap.web.input.VirtualCorpusJson;
+import de.ids_mannheim.korap.web.input.QueryJson;
 
 /**
  * VirtualCorpusService handles the logic behind
@@ -119,7 +120,7 @@
     }
 
     public List<VirtualCorpusDto> listVCByType (String username,
-            String createdBy, VirtualCorpusType type) throws KustvaktException {
+            String createdBy, ResourceType type) throws KustvaktException {
 
         boolean isAdmin = adminDao.isAdmin(username);
 
@@ -168,7 +169,7 @@
 
         if (vc.getCreatedBy().equals(username) || adminDao.isAdmin(username)) {
 
-            if (vc.getType().equals(VirtualCorpusType.PUBLISHED)) {
+            if (vc.getType().equals(ResourceType.PUBLISHED)) {
                 VirtualCorpusAccess access =
                         accessDao.retrieveHiddenAccess(vcId);
                 accessDao.deleteAccess(access, "system");
@@ -209,7 +210,7 @@
         else if (vc.getCreatedBy().equals(username)
                 || adminDao.isAdmin(username)) {
 
-            if (vc.getType().equals(VirtualCorpusType.PUBLISHED)) {
+            if (vc.getType().equals(ResourceType.PUBLISHED)) {
                 VirtualCorpusAccess access =
                         accessDao.retrieveHiddenAccess(vc.getId());
                 accessDao.deleteAccess(access, "system");
@@ -236,7 +237,7 @@
 //    }
 
     public Status handlePutRequest (String username, String vcCreator,
-            String vcName, VirtualCorpusJson vcJson) throws KustvaktException {
+            String vcName, QueryJson vcJson) throws KustvaktException {
         
         verifyUsername(username, vcCreator);
         VirtualCorpus vc = vcDao.retrieveVCByName(vcName, vcCreator);
@@ -251,7 +252,7 @@
         }
     }
 
-    public void editVC (VirtualCorpus existingVC, VirtualCorpusJson newVC,
+    public void editVC (VirtualCorpus existingVC, QueryJson newVC,
             String vcName, String username) throws KustvaktException {
 
         if (!username.equals(existingVC.getCreatedBy())
@@ -269,11 +270,11 @@
                     koralQuery);
         }
 
-        VirtualCorpusType type = newVC.getType();
+        ResourceType type = newVC.getType();
         if (type != null) {
-            if (existingVC.getType().equals(VirtualCorpusType.PUBLISHED)) {
+            if (existingVC.getType().equals(ResourceType.PUBLISHED)) {
                 // withdraw from publication
-                if (!type.equals(VirtualCorpusType.PUBLISHED)) {
+                if (!type.equals(ResourceType.PUBLISHED)) {
                     VirtualCorpusAccess hiddenAccess =
                             accessDao.retrieveHiddenAccess(existingVC.getId());
                     deleteVCAccess(hiddenAccess.getId(), "system");
@@ -284,7 +285,7 @@
                 }
                 // else remains the same
             }
-            else if (type.equals(VirtualCorpusType.PUBLISHED)) {
+            else if (type.equals(ResourceType.PUBLISHED)) {
                 publishVC(existingVC.getId());
             }
         }
@@ -314,18 +315,27 @@
         }
     }
 
-    public void storeVC (VirtualCorpusJson vc, String vcName, String createdBy)
+    public void storeVC (QueryJson vc, String vcName, String createdBy)
             throws KustvaktException {
-        ParameterChecker.checkStringValue(vc.getCorpusQuery(), "corpusQuery");
-        String koralQuery = serializeCorpusQuery(vc.getCorpusQuery());
-
-        storeVC(vcName, vc.getType(), koralQuery, vc.getDefinition(),
+        String koralQuery = null;
+        if (vc.getQueryType().equals(QueryType.VIRTUAL_CORPUS)){
+            ParameterChecker.checkStringValue(vc.getCorpusQuery(), "corpusQuery");
+            koralQuery = serializeCorpusQuery(vc.getCorpusQuery());
+        }
+        else if (vc.getQueryType().equals(QueryType.QUERY)){
+            ParameterChecker.checkStringValue(vc.getQuery(), "query");
+            ParameterChecker.checkStringValue(vc.getQueryLanguage(), "queryLanguage");
+            koralQuery = serializeQuery(vc.getQuery(), vc.getQueryLanguage());
+        }
+        
+        storeVC(vcName, vc.getType(), vc.getQueryType(), koralQuery, vc.getDefinition(),
                 vc.getDescription(), vc.getStatus(), vc.isCached(), createdBy);
     }
 
-    public void storeVC (String vcName, VirtualCorpusType type, String koralQuery,
-            String definition, String description, String status,
-            boolean isCached, String username) throws KustvaktException {
+    public void storeVC (String vcName, ResourceType type,
+            QueryType queryType, String koralQuery, String definition,
+            String description, String status, boolean isCached,
+            String username) throws KustvaktException {
         ParameterChecker.checkNameValue(vcName, "vcName");
         ParameterChecker.checkObjectValue(type, "type");
 
@@ -336,7 +346,7 @@
                     vcName);
         }
 
-        if (type.equals(VirtualCorpusType.SYSTEM) && !username.equals("system")
+        if (type.equals(ResourceType.SYSTEM) && !username.equals("system")
                 && !adminDao.isAdmin(username)) {
             throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                     "Unauthorized operation for user: " + username, username);
@@ -348,9 +358,9 @@
         if (DEBUG) jlog.debug("Storing VC " + vcName + "in the database ");
         int vcId = 0;
         try {
-            vcId = vcDao.createVirtualCorpus(vcName, type, requiredAccess,
-                    koralQuery, definition, description, status, isCached,
-                    username);
+            vcId = vcDao.createVirtualCorpus(vcName, type, queryType,
+                    requiredAccess, koralQuery, definition, description, status,
+                    isCached, username);
 
         }
         catch (Exception e) {
@@ -366,7 +376,7 @@
             throw new KustvaktException(StatusCodes.DB_INSERT_FAILED,
                     cause.getMessage());
         }
-        if (type.equals(VirtualCorpusType.PUBLISHED)) {
+        if (type.equals(ResourceType.PUBLISHED)) {
             publishVC(vcId);
         }
     }
@@ -388,6 +398,17 @@
         }
         return koralQuery;
     }
+    
+    private String serializeQuery (String query, String queryLanguage)
+            throws KustvaktException {
+        QuerySerializer serializer = new QuerySerializer();
+        String koralQuery;
+            koralQuery = serializer.setQuery(query, queryLanguage).toJSON();
+        if (DEBUG) {
+            jlog.debug(koralQuery);
+        }
+        return koralQuery;
+    }
 
     public CorpusAccess determineRequiredAccess (boolean isCached, String name,
             String koralQuery) throws KustvaktException {
@@ -474,7 +495,7 @@
                         cause.getMessage());
             }
             
-            vcDao.editVirtualCorpus(vc, null, VirtualCorpusType.PROJECT, null,
+            vcDao.editVirtualCorpus(vc, null, ResourceType.PROJECT, null,
                     null, null, null, null, vc.isCached());
         }
     }
@@ -618,7 +639,10 @@
             String createdBy) throws KustvaktException {
         VirtualCorpus vc = searchVCByName(username, vcName, createdBy);
         String json = vc.getKoralQuery();
-        String statistics = krill.getStatistics(json);
+        String statistics = null;
+        if (vc.getQueryType().equals(QueryType.VIRTUAL_CORPUS)){
+            statistics = krill.getStatistics(json);
+        }
         return converter.createVirtualCorpusDto(vc, statistics);
     }
 
@@ -634,19 +658,19 @@
 
     private void checkVCAccess (VirtualCorpus vc, String username)
             throws KustvaktException {
-        VirtualCorpusType type = vc.getType();
+        ResourceType type = vc.getType();
 
         if (!adminDao.isAdmin(username)
                 && !username.equals(vc.getCreatedBy())) {
-            if (type.equals(VirtualCorpusType.PRIVATE)
-                    || (type.equals(VirtualCorpusType.PROJECT)
+            if (type.equals(ResourceType.PRIVATE)
+                    || (type.equals(ResourceType.PROJECT)
                             && !hasAccess(username, vc.getId()))) {
                 throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
                         "Unauthorized operation for user: " + username,
                         username);
             }
 
-            else if (VirtualCorpusType.PUBLISHED.equals(type)
+            else if (ResourceType.PUBLISHED.equals(type)
                     && !username.equals("guest")) {
                 // add user in the VC's auto group
                 UserGroup userGroup = userGroupService
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
new file mode 100644
index 0000000..ff8f35b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/QueryReferenceController.java
@@ -0,0 +1,165 @@
+package de.ids_mannheim.korap.web.controller;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.SecurityContext;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import com.sun.jersey.spi.container.ResourceFilters;
+
+import de.ids_mannheim.korap.constant.OAuth2Scope;
+import de.ids_mannheim.korap.constant.QueryType;
+import de.ids_mannheim.korap.dto.VirtualCorpusDto;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.oauth2.service.OAuth2ScopeService;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.service.VirtualCorpusService;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
+import de.ids_mannheim.korap.web.filter.BlockingFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+import de.ids_mannheim.korap.web.input.QueryJson;
+
+/**
+ * QueryReferenceController defines web APIs related to the
+ * management of query references.
+ *
+ * This controller is based on VirtualCorpusController.
+ *
+ * @author diewald, margaretha
+ *
+ */
+@Controller
+@Path("{version}/query")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        BlockingFilter.class, PiwikFilter.class })
+public class QueryReferenceController {
+
+    @Autowired
+    private KustvaktResponseHandler kustvaktResponseHandler;
+    @Autowired
+    private VirtualCorpusService service;
+    @Autowired
+    private OAuth2ScopeService scopeService;
+
+    /**
+     * Creates a query reference according to the given Json.
+     * The query reference creator must be the same as the
+     * authenticated username.
+     * 
+     * TODO: In the future, this may also update a query.
+     *
+     * @param securityContext
+     * @param qCreator
+     *            the username of the vc creator, must be the same
+     *            as the authenticated username
+     * @param qName
+     *           the vc name
+     * @param query a json object describing the query and its properties
+     * @return
+     * @throws KustvaktException
+     */
+    @PUT
+    @Path("/~{qCreator}/{qName}")
+    @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    public Response createQuery (
+        @Context SecurityContext securityContext,
+        @PathParam("qCreator") String qCreator,
+        @PathParam("qName") String qName,
+        QueryJson query) throws KustvaktException {
+
+        TokenContext context =
+            (TokenContext) securityContext.getUserPrincipal();
+
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.EDIT_VC);
+            Status status = service.handlePutRequest(context.getUsername(),
+                    qCreator, qName, query);
+            return Response.status(status).build();
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+        
+    }
+
+    /**
+     * Returns the query with the given name and creator.
+     * 
+     * @param securityContext
+     * @param createdBy
+     *            query creator
+     * @param qName
+     *            query name
+     * @return the query with the given name and creator.
+     */
+    @GET
+    @Path("~{createdBy}/{qName}")
+    @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+    @ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+        DemoUserFilter.class, PiwikFilter.class })
+    public VirtualCorpusDto retrieveQueryByName (
+            @Context SecurityContext securityContext,
+            @PathParam("createdBy") String createdBy,
+            @PathParam("qName") String qName) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.VC_INFO);
+            return service.retrieveVCByName(context.getUsername(), qName,
+                    createdBy);
+        }
+        catch (KustvaktException e) {
+            throw kustvaktResponseHandler.throwit(e);
+        }
+    }
+
+    /**
+     * Only the VC owner and system admins can delete VC. VCA admins
+     * can delete VC-accesses e.g. of project VC, but not the VC
+     * themselves.
+     * 
+     * @param securityContext
+     * @param createdBy
+     *            vc creator
+     * @param vcName
+     *            vc name
+     * @return HTTP status 200, if successful
+     */
+    /*
+    @DELETE
+    @Path("~{createdBy}/{vcName}")
+    public Response deleteVCByName (@Context SecurityContext securityContext,
+            @PathParam("createdBy") String createdBy,
+            @PathParam("vcName") String vcName) {
+        TokenContext context =
+                (TokenContext) securityContext.getUserPrincipal();
+        try {
+            scopeService.verifyScope(context, OAuth2Scope.DELETE_VC);
+            service.deleteVCByName(context.getUsername(), vcName, createdBy);
+        }
+        catch (KustvaktException e) {
+        throw kustvaktResponseHandler.throwit(e);
+        }
+        return Response.ok().build();
+    };
+    */
+
+    
+    // TODO: List all queries available to the logged in user
+    // TODO: List all queries of a sepcific user
+    // TODO: Some admin routes missing.
+    // TODO: Some sharing routes missing
+};
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
index 3509d03..645329e 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/controller/VirtualCorpusController.java
@@ -23,7 +23,7 @@
 import com.sun.jersey.spi.container.ResourceFilters;
 
 import de.ids_mannheim.korap.constant.OAuth2Scope;
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
 import de.ids_mannheim.korap.dto.VirtualCorpusDto;
 import de.ids_mannheim.korap.exceptions.KustvaktException;
@@ -36,7 +36,7 @@
 import de.ids_mannheim.korap.web.filter.BlockingFilter;
 import de.ids_mannheim.korap.web.filter.DemoUserFilter;
 import de.ids_mannheim.korap.web.filter.PiwikFilter;
-import de.ids_mannheim.korap.web.input.VirtualCorpusJson;
+import de.ids_mannheim.korap.web.input.QueryJson;
 
 /**
  * VirtualCorpusController defines web APIs related to virtual corpus
@@ -93,7 +93,7 @@
     public Response createUpdateVC (@Context SecurityContext securityContext,
             @PathParam("vcCreator") String vcCreator,
             @PathParam("vcName") String vcName,
-            VirtualCorpusJson vc) throws KustvaktException {
+            QueryJson vc) throws KustvaktException {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
 
@@ -213,7 +213,7 @@
      * @param createdBy
      *            username of virtual corpus creator
      * @param type
-     *            {@link VirtualCorpusType}
+     *            {@link ResourceType}
      * @return a list of virtual corpora
      */
     @GET
@@ -222,7 +222,7 @@
     public List<VirtualCorpusDto> listVCByType (
             @Context SecurityContext securityContext,
             @QueryParam("createdBy") String createdBy,
-            @QueryParam("type") VirtualCorpusType type) {
+            @QueryParam("type") ResourceType type) {
         TokenContext context =
                 (TokenContext) securityContext.getUserPrincipal();
         try {
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java b/full/src/main/java/de/ids_mannheim/korap/web/input/QueryJson.java
similarity index 62%
rename from full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java
rename to full/src/main/java/de/ids_mannheim/korap/web/input/QueryJson.java
index 1f14f9c..0e312d7 100644
--- a/full/src/main/java/de/ids_mannheim/korap/web/input/VirtualCorpusJson.java
+++ b/full/src/main/java/de/ids_mannheim/korap/web/input/QueryJson.java
@@ -1,7 +1,8 @@
 package de.ids_mannheim.korap.web.input;
 
 
-import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.constant.QueryType;
+import de.ids_mannheim.korap.constant.ResourceType;
 import de.ids_mannheim.korap.service.VirtualCorpusService;
 import de.ids_mannheim.korap.web.controller.VirtualCorpusController;
 import lombok.Getter;
@@ -16,16 +17,22 @@
  */
 @Getter
 @Setter
-public class VirtualCorpusJson {
+public class QueryJson {
     // default false
     private boolean isCached;
     
-    // required in creating VCs
-    private VirtualCorpusType type;
+    // required
+    private ResourceType type;
+    private QueryType queryType;
+    // required for queryType="VIRTUAL_CORPUS"
     private String corpusQuery;
+    // required for queryType="QUERY"
+    private String query;
+    private String queryLanguage;
     
     // optional
     private String definition;
     private String description;
     private String status;
+    private String queryVersion;
 }
\ No newline at end of file