Merged core to full
Change-Id: I47cbb4c98e6a7d549c287c27419177bd08b72608
diff --git a/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java b/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java
new file mode 100644
index 0000000..ccb6d1a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/annotation/AnnotationParser.java
@@ -0,0 +1,244 @@
+package de.ids_mannheim.korap.annotation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.dao.AnnotationDao;
+
+/** Parser for extracting annotation descriptions from Kalamar javascripts
+ *
+ * @author margaretha
+ *
+ */
+@Deprecated
+@Component
+public class AnnotationParser {
+
+ private Logger log = LogManager.getLogger(AnnotationDao.class);
+
+ public static final Pattern quotePattern = Pattern.compile("\"([^\"]*)\"");
+
+ @Autowired
+ private AnnotationDao annotationDao;
+
+ private Annotation foundry = null;
+ private AnnotationLayer layer = null;
+ private AnnotationKey key = null;
+
+ private Set<AnnotationKey> keys = new HashSet<>();
+ private Set<Annotation> values = new HashSet<>();
+
+ public void run () throws IOException {
+ PathMatchingResourcePatternResolver resolver =
+ new PathMatchingResourcePatternResolver(
+ getClass().getClassLoader());
+ Resource[] resources = resolver
+ .getResources("classpath:annotation-scripts/foundries/*.js");
+
+ if (resources.length < 1) return;
+
+ for (Resource r : resources) {
+// log.debug(r.getFilename());
+ readFile(r.getInputStream());
+ }
+ }
+
+ private void readFile (InputStream inputStream) throws IOException {
+ BufferedReader br =
+ new BufferedReader(new InputStreamReader(inputStream), 1024);
+
+ foundry = null;
+
+ String line, annotationCode = "", annotationType = "";
+ Matcher m;
+ ArrayList<String> array;
+ while ((line = br.readLine()) != null) {
+ line = line.trim();
+ if (line.startsWith("ah")) {
+ m = quotePattern.matcher(line);
+ if (m.find()) {
+ annotationCode = m.group(1);
+ annotationType = computeAnnotationType(annotationCode);
+ }
+ m.reset();
+ }
+ else if (line.startsWith("];")) {
+ if (!keys.isEmpty()) {
+ layer.setKeys(keys);
+ annotationDao.updateAnnotationLayer(layer);
+ }
+ if (!values.isEmpty()) {
+ key.setValues(values);
+ annotationDao.updateAnnotationKey(key);
+ }
+ keys.clear();
+ values.clear();
+ layer = null;
+ key = null;
+ }
+ else if (line.startsWith("[")) {
+ array = computeValues(line);
+ parseArray(annotationCode, annotationType, array);
+ }
+
+ }
+ br.close();
+ }
+
+ public static ArrayList<String> computeValues (String line) {
+ ArrayList<String> values;
+ Matcher m = quotePattern.matcher(line);
+ values = new ArrayList<String>();
+ while (m.find()) {
+ values.add(m.group(1));
+ }
+ return values;
+ }
+
+ private void parseArray (String annotationCode, String annotationType,
+ ArrayList<String> array) {
+ if (annotationType.equals(AnnotationType.FOUNDRY)) {
+ String code = array.get(1).substring(0, array.get(1).length() - 1);
+ foundry = retrieveOrCreateAnnotation(code, AnnotationType.FOUNDRY,
+ null, array.get(0));
+ }
+ else if (annotationType.equals(AnnotationType.LAYER)) {
+ String code = array.get(1);
+ if (code.endsWith("=")) {
+ code = code.substring(0, code.length() - 1);
+ }
+ Annotation layer = retrieveOrCreateAnnotation(code, annotationType,
+ null, array.get(0));
+ try {
+ AnnotationLayer annotationLayer =
+ annotationDao.retrieveAnnotationLayer(foundry.getCode(),
+ layer.getCode());
+ if (annotationLayer == null) {
+ annotationDao.createAnnotationLayer(foundry, layer);
+ }
+ }
+ catch (Exception e) {
+ log.debug("Duplicate annotation layer: " + foundry.getCode()
+ + "/" + layer.getCode());
+ }
+ }
+ else if (annotationType.equals(AnnotationType.KEY))
+
+ {
+ if (layer == null) {
+ computeLayer(annotationCode);
+ }
+
+ Annotation annotation = null;
+ if (array.size() == 2) {
+ String code = array.get(1);
+ if (code.endsWith("=") || code.endsWith(":")) {
+ code = code.substring(0, code.length() - 1);
+ }
+ annotation = retrieveOrCreateAnnotation(code, annotationType,
+ null, array.get(0));
+ }
+ else if (array.size() == 3) {
+ annotation = retrieveOrCreateAnnotation(array.get(0),
+ annotationType, array.get(1), array.get(2));
+ }
+ if (annotation != null) {
+ AnnotationKey annotationKey =
+ annotationDao.retrieveAnnotationKey(layer, annotation);
+ if (annotationKey == null) {
+ annotationDao.createAnnotationKey(layer, annotation);
+ }
+ this.keys.add(annotationKey);
+ }
+ }
+ else if (annotationType.equals(AnnotationType.VALUE)) {
+ if (this.key == null) {
+ computeKey(annotationCode);
+ }
+ Annotation value = retrieveOrCreateAnnotation(array.get(0),
+ AnnotationType.VALUE, array.get(1), array.get(2));
+ if (value != null) {
+ values.add(value);
+ }
+ }
+ }
+
+ private void computeKey (String code) {
+ String[] codes = code.split("=");
+ if (codes.length > 1) {
+ computeLayer(codes[0]);
+ String keyCode = codes[1];
+ if (keyCode.endsWith(":") || keyCode.endsWith("-")) {
+ keyCode = keyCode.substring(0, keyCode.length() - 1);
+ }
+ Annotation key = annotationDao.retrieveAnnotation(keyCode,
+ AnnotationType.KEY);
+ this.key = annotationDao.retrieveAnnotationKey(layer, key);
+ }
+
+ }
+
+ private void computeLayer (String code) {
+ String[] codes = code.split("/");
+ if (codes.length > 1) {
+ String layerCode = codes[1];
+ if (layerCode.endsWith("=")) {
+ layerCode = layerCode.substring(0, layerCode.length() - 1);
+ }
+ this.layer =
+ annotationDao.retrieveAnnotationLayer(codes[0], layerCode);
+ if (layer == null) {
+ log.warn("Layer is null for " + code);
+ }
+ }
+ }
+
+ private Annotation retrieveOrCreateAnnotation (String code, String type,
+ String text, String description) {
+ Annotation annotation = annotationDao.retrieveAnnotation(code, type);
+ if (annotation == null) {
+ annotation = annotationDao.createAnnotation(code, type, text,
+ description);
+ }
+ return annotation;
+ }
+
+ private String computeAnnotationType (String code) {
+ String[] codes = code.split("/");
+ if (codes.length == 1) {
+ if (codes[0].equals("-")) {
+ return AnnotationType.FOUNDRY;
+ }
+ return AnnotationType.LAYER;
+ }
+ else if (codes.length == 2) {
+ if (codes[1].endsWith(":") || codes[1].endsWith("-")) {
+ return AnnotationType.VALUE;
+ }
+ else {
+ return AnnotationType.KEY;
+ }
+ }
+
+ return "unknown";
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java b/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java
new file mode 100644
index 0000000..8740919
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/annotation/ArrayVariables.java
@@ -0,0 +1,95 @@
+package de.ids_mannheim.korap.annotation;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+
+/**
+ * Helper class to parse annotation scripts variables. It prints out
+ * corenlp constituency layer for each negranodes.
+ *
+ * @author margaretha
+ *
+ */
+@Deprecated
+public class ArrayVariables {
+
+ public static HashMap<String, List<Annotation>> annotationMap =
+ new HashMap<>();
+
+ public static void main (String[] args) throws IOException {
+ ArrayVariables variables = new ArrayVariables();
+ variables.extractVariables();
+
+ List<Annotation> negranodes = annotationMap.get("negranodes");
+ for (Annotation n : negranodes) {
+ System.out.println("ah[\"corenlp/c=" + n.getCode() + "-\"] = [");
+ int i = 1;
+ List<Annotation> negraedges = annotationMap.get("negraedges");
+ for (Annotation edge : negraedges) {
+ System.out.print(
+ " [\"" + edge.getCode() + "\", \"" + edge.getText()
+ + "\", \"" + edge.getDescription() + "\"]");
+ if (i < negraedges.size()) {
+ System.out.println(",");
+ }
+ else {
+ System.out.println();
+ }
+ i++;
+ }
+ System.out.println("];");
+ System.out.println();
+ }
+ }
+
+ public void extractVariables () throws IOException {
+ String dir = "annotation-scripts/variables";
+ if (dir.isEmpty()) return;
+
+ File d = new File(dir);
+ if (!d.isDirectory()) {
+ throw new IOException("Directory " + dir + " is not valid");
+ }
+
+ for (File file : d.listFiles()) {
+ if (!file.exists()) {
+ throw new IOException("File " + file + " is not found.");
+ }
+ readFile(file);
+ }
+
+ }
+
+ private void readFile (File file) throws IOException {
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(file)));
+
+ String line;
+ ArrayList<String> values;
+ List<Annotation> annotations = new ArrayList<>();
+ while ((line = br.readLine()) != null) {
+ line = line.trim();
+ if (line.startsWith("[")) {
+ values = AnnotationParser.computeValues(line);
+
+ Annotation annotation = new Annotation(values.get(0),
+ AnnotationType.VALUE, values.get(1), values.get(2));
+ annotations.add(annotation);
+ }
+ }
+ br.close();
+
+ String filename = file.getName();
+ filename = filename.substring(0, filename.length() - 3);
+ annotationMap.put(filename, annotations);
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java b/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
new file mode 100644
index 0000000..5370c9f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/auditing/AuditRecord.java
@@ -0,0 +1,173 @@
+package de.ids_mannheim.korap.auditing;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Arrays;
+import java.util.Date;
+
+/**
+ * @author hanl
+ * <p/>
+ * Record holder for auditing requests. Holds the data until
+ * it can be persisted to a database
+ */
+@Getter
+@Setter
+public class AuditRecord {
+
+ // todo: handle via status codes
+ @Deprecated
+ public enum Operation {
+ GET, INSERT, UPDATE, DELETE, CREATE
+ }
+
+ public enum CATEGORY {
+ SECURITY, DATABASE, RESOURCE, QUERY, SERVICE
+ }
+
+ @JsonIgnore
+ private Integer id;
+ //security access describes changes in user authorities and access control permissions of resources
+ private String userid;
+ private String target;
+
+ //fixme: replace with more specific error codes
+ private CATEGORY category;
+ private String loc;
+ private Long timestamp;
+ private Integer status = -1;
+ private String args;
+ private String field_1 = "None";
+
+
+ private AuditRecord () {
+ this.timestamp = TimeUtils.getNow().getMillis();
+ }
+
+
+ public AuditRecord (CATEGORY category) {
+ this();
+ this.category = category;
+ }
+
+
+ public AuditRecord (CATEGORY cat, Object userID, Integer status) {
+ this(cat);
+ this.status = status;
+ if (userID != null) {
+ //todo: client info!
+ // this.loc = clientInfoToString(user.getTokenContext().getHostAddress(),
+ // user.getTokenContext().getUserAgent());
+ this.loc = clientInfoToString("null", "null");
+ userid = String.valueOf(userID);
+ }
+ else {
+ this.loc = clientInfoToString("null", "null");
+ userid = "-1";
+ }
+ }
+
+
+ public static AuditRecord serviceRecord (Object user, Integer status,
+ String ... args) {
+ AuditRecord r = new AuditRecord(CATEGORY.SERVICE);
+ r.setArgs(Arrays.asList(args).toString());
+ r.setUserid(String.valueOf(user));
+ r.setStatus(status);
+ return r;
+ }
+
+
+ public static AuditRecord dbRecord (Object user, Integer status,
+ String ... args) {
+ AuditRecord r = new AuditRecord(CATEGORY.DATABASE);
+ r.setArgs(Arrays.asList(args).toString());
+ r.setUserid(String.valueOf(user));
+ r.setStatus(status);
+ return r;
+ }
+
+
+ public AuditRecord fromJson (String json) throws KustvaktException {
+ JsonNode n = JsonUtils.readTree(json);
+ AuditRecord r = new AuditRecord();
+ r.setCategory(CATEGORY.valueOf(n.path("category").asText()));
+ r.setTarget(n.path("target").asText());
+ r.setField_1(n.path("field_1").asText());
+ r.setUserid(n.path("account").asText());
+ r.setStatus(n.path("status").asInt());
+ r.setLoc(n.path("loc").asText());
+ return r;
+ }
+
+
+ private String clientInfoToString (String IP, String userAgent) {
+ return userAgent + "@" + IP;
+ }
+
+
+ // fixme: add id, useragent
+ @Override
+ public String toString () {
+ StringBuilder b = new StringBuilder();
+ b.append(category.toString().toLowerCase() + " audit : ")
+ .append(userid + "@" + new Date(timestamp)).append("\n")
+ .append("Status " + status).append("; ");
+
+ if (this.args != null)
+ b.append("Args " + field_1).append("; ");
+ if (this.loc != null)
+ b.append("Location " + loc).append("; ");
+ return b.toString();
+ }
+
+
+ @Override
+ public boolean equals (Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ AuditRecord that = (AuditRecord) o;
+
+ if (userid != null ? !userid.equals(that.userid) : that.userid != null)
+ return false;
+ if (category != that.category)
+ return false;
+ if (status != null ? !status.equals(that.status) : that.status != null)
+ return false;
+ if (field_1 != null ? !field_1.equals(that.field_1)
+ : that.field_1 != null)
+ return false;
+ if (loc != null ? !loc.equals(that.loc) : that.loc != null)
+ return false;
+ if (target != null ? !target.equals(that.target) : that.target != null)
+ return false;
+ if (timestamp != null ? !timestamp.equals(that.timestamp)
+ : that.timestamp != null)
+ return false;
+
+ return true;
+ }
+
+
+ @Override
+ public int hashCode () {
+ int result = userid != null ? userid.hashCode() : 0;
+ result = 31 * result + (target != null ? target.hashCode() : 0);
+ result = 31 * result + category.hashCode();
+ result = 31 * result + (loc != null ? loc.hashCode() : 0);
+ result = 31 * result + (timestamp != null ? timestamp.hashCode() : 0);
+ result = 31 * result + (status != null ? status.hashCode() : 0);
+ result = 31 * result + (field_1 != null ? field_1.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java
new file mode 100644
index 0000000..47d0738
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationIface.java
@@ -0,0 +1,27 @@
+package de.ids_mannheim.korap.authentication;
+
+import java.util.Map;
+
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+
+public interface AuthenticationIface {
+
+ public TokenContext getTokenContext(String authToken) throws KustvaktException;
+
+
+ public TokenContext createTokenContext(User user, Map<String, Object> attr)
+ throws KustvaktException;
+
+
+ void removeUserSession (String token) throws KustvaktException;
+
+
+ public TokenContext refresh (TokenContext context) throws KustvaktException;
+
+
+ public TokenType getTokenType ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java
new file mode 100644
index 0000000..2fcc4e9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/AuthenticationManager.java
@@ -0,0 +1,96 @@
+package de.ids_mannheim.korap.authentication;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.core.HttpHeaders;
+
+import de.ids_mannheim.korap.config.KustvaktCacheable;
+import de.ids_mannheim.korap.constant.AuthenticationMethod;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.Userdata;
+
+/**
+ * @author hanl
+ * @date 15/06/2015
+ */
+public abstract class AuthenticationManager extends KustvaktCacheable {
+
+ private Map<TokenType, AuthenticationIface> providers;
+
+ public AuthenticationManager () {
+ super();
+ }
+
+ public AuthenticationManager (String cache) {
+ super(cache, "key:"+cache);
+ this.providers = new HashMap<>();
+ }
+
+ public void setProviders (Set<AuthenticationIface> providers) {
+ for (AuthenticationIface i : providers) {
+ this.providers.put(i.getTokenType(), i);
+ }
+ }
+
+ protected AuthenticationIface getProvider (TokenType scheme,
+ TokenType default_iface) {
+
+ // Debug FB: loop a Map
+
+ /*for (Map.Entry<String, AuthenticationIface> entry : this.providers.entrySet())
+ {
+ System.out.println("Debug: provider: Key : " + entry.getKey() + " Value : " + entry.getValue());
+ }
+ */
+ // todo: configurable authentication schema
+ if (scheme == null) {
+ return this.providers.get(default_iface);
+ }
+ else {
+ return this.providers.get(scheme);
+ }
+ }
+
+ public abstract TokenContext getTokenContext (TokenType type, String token,
+ String host, String useragent) throws KustvaktException;
+
+ public abstract User getUser (String username) throws KustvaktException;
+
+ public abstract boolean isRegistered (String id);
+
+ public abstract User authenticate (AuthenticationMethod method,
+ String username, String password, Map<String, Object> attributes)
+ throws KustvaktException;
+
+ public abstract TokenContext createTokenContext (User user,
+ Map<String, Object> attr, TokenType type) throws KustvaktException;
+
+ public abstract void setAccessAndLocation (User user, HttpHeaders headers);
+
+ public abstract void logout (TokenContext context) throws KustvaktException;
+
+ public abstract void lockAccount (User user) throws KustvaktException;
+
+ public abstract boolean deleteAccount (User user) throws KustvaktException;
+
+ @Deprecated
+ public abstract <T extends Userdata> T getUserData (User user,
+ Class<T> clazz) throws KustvaktException;
+
+ @Deprecated
+ public abstract void updateUserData (Userdata data)
+ throws KustvaktException;
+
+ public String providerList () {
+ return "provider list: " + this.providers.toString();
+ }
+
+ public abstract User getUser (String username, String method)
+ throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java
new file mode 100644
index 0000000..236f26a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/AuthorizationData.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import lombok.Getter;
+import lombok.Setter;
+
+/** Describes the values stored in Authorization header of HTTP requests.
+ *
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+public class AuthorizationData {
+
+ private String token;
+ private AuthenticationScheme authenticationScheme;
+ private String username;
+ private String password;
+
+}
+
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java
new file mode 100644
index 0000000..241e9ce
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/HttpAuthorizationHandler.java
@@ -0,0 +1,66 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * Implementation of Basic HTTP authentication scheme (see RFC 7253
+ * and 7617) for client asking for authorization and sending user
+ * data.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+public class HttpAuthorizationHandler {
+
+ public static String createBasicAuthorizationHeaderValue (String username,
+ String password) throws KustvaktException {
+ ParameterChecker.checkStringValue(username, "username");
+ ParameterChecker.checkStringValue(password, "password");
+
+ String credentials = TransferEncoding.encodeBase64(username, password);
+ return AuthenticationScheme.BASIC.displayName() + " " + credentials;
+ }
+
+ public AuthorizationData parseAuthorizationHeaderValue (
+ String authorizationHeader) throws KustvaktException {
+ ParameterChecker.checkStringValue(authorizationHeader,
+ "authorization header");
+
+ String[] values = authorizationHeader.split(" ");
+ if (values.length != 2) {
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+ "Cannot parse authorization header value "
+ + authorizationHeader
+ + ". Use this format: [authentication "
+ + "scheme] [authentication token]",
+ authorizationHeader);
+ }
+
+ AuthorizationData data = new AuthorizationData();
+ String scheme = values[0];
+ try {
+ data.setAuthenticationScheme(
+ AuthenticationScheme.valueOf(scheme.toUpperCase()));
+ }
+ catch (IllegalArgumentException e) {
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+ "Authentication scheme is not supported.", scheme);
+ }
+ data.setToken(values[1]);
+ return data;
+ }
+
+ public AuthorizationData parseBasicToken (AuthorizationData data)
+ throws KustvaktException {
+ String[] credentials = TransferEncoding.decodeBase64(data.getToken());
+ data.setUsername(credentials[0]);
+ data.setPassword(credentials[1]);
+ return data;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java b/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java
new file mode 100644
index 0000000..52d1a90
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/authentication/http/TransferEncoding.java
@@ -0,0 +1,53 @@
+package de.ids_mannheim.korap.authentication.http;
+
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/** TransferEncoding contains encoding and decoding methods for data transfer,
+ * e.g. transfering credentials using basic Http authentication.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+public class TransferEncoding {
+
+ /** Encodes username and password using Base64.
+ *
+ * @param username username
+ * @param password password
+ * @return
+ */
+ public static String encodeBase64 (String username, String password) {
+ String s = username + ":" + password;
+ return new String(Base64.encodeBase64(s.getBytes()));
+ }
+
+ /** Decodes the given string using Base64.
+ *
+ * @param encodedStr
+ * @return username and password as an array of strings.
+ * @throws KustvaktException
+ */
+ public static String[] decodeBase64 (String encodedStr)
+ throws KustvaktException {
+
+ ParameterChecker.checkStringValue(encodedStr, "encoded string");
+ String decodedStr = new String(Base64.decodeBase64(encodedStr));
+
+ if (decodedStr.contains(":") && decodedStr.split(":").length == 2) {
+ String[] strArr = decodedStr.split(":");
+ if ((strArr[0] != null && !strArr[0].isEmpty())
+ && (strArr[1] != null && !strArr[1].isEmpty())) {
+ return decodedStr.split(":");
+ }
+
+ }
+
+ throw new IllegalArgumentException(
+ "Unknown Base64 encoding format: " + decodedStr);
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java b/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java
new file mode 100644
index 0000000..a3e2433
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Attributes.java
@@ -0,0 +1,181 @@
+package de.ids_mannheim.korap.config;
+
+public class Attributes {
+
+ // EM: openid auth_time
+ public static final String AUTHENTICATION_TIME = "auth_time";
+ public static final String DEFAULT_TIME_ZONE = "Europe/Berlin";
+ // -- EM
+
+ public static final String AUTHORIZATION = "Authorization";
+ // moved to de.ids_mannheim.korap.config.AuthenticationScheme
+// public static final String SESSION_AUTHENTICATION = "session_token";
+// public static final String API_AUTHENTICATION = "api_token";
+// public static final String OAUTH2_AUTHORIZATION = "bearer";
+// public static final String OPENID_AUTHENTICATION = "id_token";
+// public static final String BASIC_AUTHENTICATION = "basic";
+
+ public static final String LOCATION = "location"; // location of Client: User.INTERN/EXTERN
+ public static final String CORPUS_ACCESS = "corpusAccess"; // User.ALL/PUB/FREE.
+
+ public static final String CLIENT_ID = "client_id";
+ public static final String CLIENT_SECRET = "client_secret";
+ public static final String SCOPE = "scope";
+
+ public static final String PUBLIC_GROUP = "public";
+
+ public static final String SERVICE_ACCESS = "service_access";
+ public static final String USER = "KorapUser";
+ public static final String SHIBUSER = "ShibUser";
+ public static final String DEMO_DISPLAY = "Anonymous";
+ public static final String DEMOUSER_PASSWORD = "demo";
+
+ public static final String SETTINGS = "LocalSettings";
+ // public static final String STORAGE_SETTINGS = "StorageSettings";
+
+ public static final String QUERY_ABBREVIATION = "Q";
+ public static final String LAYER = "layer";
+
+ public static final String TYPE = "type";
+
+ public static final String ID = "ID";
+ @Deprecated
+ //refactor
+ public static final String UID = "accountID";
+ public static final String USERNAME = "username";
+ public static final String PASSWORD = "password";
+ public static final String GENDER = "gender";
+ public static final String FIRSTNAME = "firstName";
+ public static final String LASTNAME = "lastName";
+ public static final String PHONE = "phone";
+ public static final String INSTITUTION = "institution";
+ public static final String EMAIL = "email";
+ public static final String ADDRESS = "address";
+ public static final String COUNTRY = "country";
+ public static final String IPADDRESS = "ipaddress";
+ public static final String IS_ADMIN = "admin";
+ // deprecated, use created
+ public static final String ACCOUNT_CREATION = "account_creation";
+ public static final String ACCOUNTLOCK = "account_lock";
+ public static final String ACCOUNTLINK = "account_link";
+ public static final String URI = "uri";
+ public static final String URI_FRAGMENT = "uri_fragment";
+ public static final String URI_EXPIRATION = "uri_expiration";
+ public static final String PRIVATE_USAGE = "privateUsage";
+
+ /**
+ * token context
+ */
+ public static final String TOKEN = "token";
+ public static final String TOKEN_TYPE = "token_type";
+ public static final String TOKEN_EXPIRATION = "expires";
+ public static final String TOKEN_CREATION = "tokenCreated";
+ public static final String USER_AGENT = "User-Agent";
+ public static final String HOST = "userIP";
+
+ public static final String QUERY_PARAM_URI = "uri";
+ public static final String QUERY_PARAM_USER = "user";
+
+ /**
+ * shibboleth attribute names
+ */
+ public static final String EPPN = "eppn";
+ public static final String COMMON_NAME = "cn";
+ public static final String SURNAME = "sn";
+
+ public static final String EDUPERSON = "eduPersonPrincipalName";
+ public static final String CN = "cn";
+ public static final String MAIL = "mail";
+ public static final String EDU_AFFIL = "eduPersonScopedAffiliation";
+
+ /**
+ * resource mappings
+ */
+
+ public static final String RID = "id";
+ public static final String OWNER = "owner";
+ public static final String NAME = "name";
+ public static final String DESCRIPTION = "description";
+
+ public static final String CORPUS_SIGLE = "corpusSigle";
+ public static final String DOC_SIGLE = "docSigle";
+ public static final String TEXT_SIGLE = "textSigle";
+
+ public static final String AVAILABILITY = "availability";
+
+ public static final String REF_CORPUS = "refCorpus";
+ public static final String QUERY = "query";
+ public static final String CACHE = "cache";
+ public static final String DOCIDS = "docIDs";
+ public static final String FOUNDRIES = "foundries";
+ public static final String DEFAULT_VALUE = "defaultColl";
+
+ public static final String FILE_FORMAT_FOR_EXPORT = "fileFormatForExport";
+ public static final String FILENAME_FOR_EXPORT = "fileNameForExport";
+ @Deprecated
+ public static final String ITEM_FOR_SIMPLE_ANNOTATION = "itemForSimpleAnnotation";
+ public static final String LEFT_CONTEXT_ITEM_FOR_EXPORT = "leftContextItemForExport";
+ public static final String LEFT_CONTEXT_SIZE_FOR_EXPORT = "leftContextSizeForExport";
+ public static final String LOCALE = "locale";
+ public static final String LEFT_CONTEXT_ITEM = "leftContextItem";
+ public static final String LEFT_CONTEXT_SIZE = "leftContextSize";
+ public static final String RIGHT_CONTEXT_ITEM = "rightContextItem";
+ public static final String RIGHT_CONTEXT_ITEM_FOR_EXPORT = "rightContextItemForExport";
+ public static final String RIGHT_CONTEXT_SIZE = "rightContextSize";
+ public static final String RIGHT_CONTEXT_SIZE_FOR_EXPORT = "rightContextSizeForExport";
+ public static final String SELECTED_COLLECTION = "selectedCollection";
+ public static final String QUERY_LANGUAGE = "queryLanguage";
+ public static final String PAGE_LENGTH = "pageLength";
+ public static final String METADATA_QUERY_EXPERT_MODUS = "metadataQueryExpertModus";
+ @Deprecated
+ public static final String SEARCH_SETTINGS_TAB = "searchSettingsTab";
+ @Deprecated
+ public static final String SELECTED_BROWSER_PROPERTY = "selectedBrowserProperty";
+ @Deprecated
+ public static final String SELECTED_CONTEXT_ITEM = "selectedContextItem";
+ @Deprecated
+ public static final String SELECTED_GRAPH_TYPE = "selectedGraphType";
+ @Deprecated
+ public static final String SELECTED_SORT_TYPE = "selectedSortType";
+ @Deprecated
+ public static final String SELECTED_VIEW_FOR_SEARCH_RESULTS = "selectedViewForSearchResults";
+ public static final String COLLECT_AUDITING_DATA = "collectData";
+
+ /**
+ * default layers
+ */
+ public static final String DEFAULT_FOUNDRY_POS = "pos-foundry";
+ public static final String DEFAULT_FOUNDRY_LEMMA = "lemma-foundry";
+ public static final String DEFAULT_FOUNDRY_CONSTITUENT = "constituent-foundry";
+ public static final String DEFAULT_FOUNDRY_RELATION = "relation-foundry";
+ public static final String DEFAULT_FOUNDRY_MORPHOLOGY = "morphology-foundry";
+ public static final String DEFAULT_FOUNDRY_STRUCTURE = "structure-foundry";
+
+ /**
+ * db column keys
+ */
+
+ public static final String SELF_REF = "self";
+
+ public static final String SYM_USE = "sym_use";
+ public static final String COMMERCIAL = "commercial";
+ public static final String LICENCE = "licence";
+ public static final String QUERY_ONLY = "query_only";
+ public static final String EXPORT = "export";
+ public static final String TIME_SPANS = "spans";
+ public static final String RANGE = "range";
+
+ public static final String GROUP_ID = "group_id";
+ public static final String CREATED = "created";
+ public static final String CREATOR = "creator";
+ public static final String ENABLED = "enabled";
+ public static final String EXPIRE = "expired";
+ public static final String TARGET_ID = "target_id";
+ public static final String IP_RANG = "ip_range";
+ public static final String PERSISTENT_ID = "persistent_id";
+ public static final String DISABLED = "disabled";
+ public static final String USER_ID = "user_id";
+ public static final String PARENT_ID = "parent_id";
+ // public static final String
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java b/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java
new file mode 100644
index 0000000..54f432e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/BeanInjectable.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.config;
+
+/**
+ * @author hanl
+ * @date 26/02/2016
+ */
+public interface BeanInjectable {
+
+ <T extends ContextHolder> void insertBeans (T beans);
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java b/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java
new file mode 100644
index 0000000..46d319e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/BeansFactory.java
@@ -0,0 +1,136 @@
+package de.ids_mannheim.korap.config;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.FileSystemXmlApplicationContext;
+
+import de.ids_mannheim.korap.interfaces.KustvaktTypeInterface;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * User: hanl
+ * Date: 10/9/13
+ * Time: 11:20 AM
+ */
+public class BeansFactory {
+
+ private static ContextHolder beanHolder;
+
+
+ //todo: allow this for external plugin systems that are not kustvakt specific
+ @Deprecated
+ public static void setCustomBeansHolder (ContextHolder holder) {
+ beanHolder = holder;
+ }
+
+
+ public static synchronized ContextHolder getKustvaktContext () {
+ return beanHolder;
+ }
+
+
+ public static synchronized ContextHolder getKustvaktContext (int i) {
+ return beanHolder;
+ }
+
+
+ public static synchronized TypeBeanFactory getTypeFactory () {
+ return new TypeBeanFactory();
+ }
+
+
+ public static int loadClasspathContext (String ... files) {
+ ApplicationContext context;
+ if (files.length == 0)
+ throw new IllegalArgumentException("Spring XML config file is not specified.");
+ else
+ context = new ClassPathXmlApplicationContext(files);
+ ContextHolder h = new ContextHolder(context) {};
+ BeansFactory.beanHolder = h;
+ // return BeansFactory.beanHolder.indexOf(h);
+ return 0;
+ }
+
+
+ public static synchronized int addApplicationContext (
+ ApplicationContext context) {
+ ContextHolder h = new ContextHolder(context) {};
+ BeansFactory.beanHolder = h;
+ // return BeansFactory.beanHolder.indexOf(h);
+ return 0;
+ }
+
+
+ public static synchronized void setKustvaktContext (ContextHolder holder) {
+ BeansFactory.beanHolder = holder;
+ }
+
+
+ public static synchronized int setApplicationContext (
+ ApplicationContext context) {
+ ContextHolder h = new ContextHolder(context) {};
+ BeansFactory.beanHolder = h;
+ return 0;
+ }
+
+
+ public static synchronized int loadFileContext (String filepath) {
+ ApplicationContext context = new FileSystemXmlApplicationContext(
+ "file:" + filepath);
+ ContextHolder h = new ContextHolder(context) {};
+ BeansFactory.beanHolder = h;
+ return 0;
+ }
+
+
+ public static void closeApplication () {
+ BeansFactory.beanHolder = null;
+ }
+
+
+ //todo: set response handler
+ @Deprecated
+ public static CoreResponseHandler getResponseHandler () {
+ return null;
+ }
+
+
+ public BeansFactory () {}
+
+ public static class TypeBeanFactory {
+
+ public <T> T getTypeInterfaceBean (Collection objs, Class type) {
+ for (Object o : objs) {
+ if (o instanceof KustvaktTypeInterface) {
+ Class t = ((KustvaktTypeInterface) o).type();
+ if (type.equals(t))
+ return (T) o;
+ }
+ }
+ throw new RuntimeException(
+ "Could not find typed bean in context for class '" + type
+ + "'");
+ }
+
+
+ @Deprecated
+ public <T> T getTypedBean (Collection objs, Class type) {
+ for (Object o : objs) {
+ Type gtype = o.getClass().getGenericSuperclass();
+ if (gtype instanceof ParameterizedType) {
+ ParameterizedType ptype = (ParameterizedType) gtype;
+ Object ctype = ptype.getActualTypeArguments()[0];
+ if (ctype.equals(type))
+ return (T) o;
+ }
+ }
+ throw new RuntimeException(
+ "Could not find typed bean in context for class '" + type
+ + "'");
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java
new file mode 100644
index 0000000..9fcc3bd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ConfigLoader.java
@@ -0,0 +1,57 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Created by hanl on 08.06.16.
+ */
+public class ConfigLoader {
+
+ private static final Logger jlog = LogManager.getLogger(ConfigLoader.class);
+
+
+ private ConfigLoader () {}
+
+
+ public static InputStream loadConfigStream (String name) {
+ InputStream stream = null;
+ try {
+ File f = new File(System.getProperty("user.dir"), name);
+
+ if (f.exists()) {
+ jlog.info("Loading config '" + name + "' from file!");
+ stream = new FileInputStream(f);
+ }
+ else {
+ jlog.info("Loading config '" + name + "' from classpath!");
+ stream = ConfigLoader.class.getClassLoader().getResourceAsStream(
+ name);
+ }
+ }
+ catch (IOException e) {
+ // do nothing
+ }
+ if (stream == null)
+ throw new RuntimeException("Config file '"+name+"' could not be loaded ...");
+ return stream;
+ }
+
+
+ public static Properties loadProperties (String name){
+ Properties p = new Properties();
+ try {
+ p.load(loadConfigStream(name));
+ } catch (IOException e) {
+ throw new RuntimeException("Properties from config file '"+name+"' could not be loaded ...");
+ }
+ return p;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Configurable.java b/full/src/main/java/de/ids_mannheim/korap/config/Configurable.java
new file mode 100644
index 0000000..af656a2
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Configurable.java
@@ -0,0 +1,13 @@
+package de.ids_mannheim.korap.config;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author hanl
+ * @date 27/07/2015
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Configurable {
+ String value();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java b/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java
new file mode 100644
index 0000000..99c54ae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ContextHolder.java
@@ -0,0 +1,98 @@
+package de.ids_mannheim.korap.config;
+
+import java.util.Collection;
+
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.interfaces.db.PersistenceClient;
+import de.ids_mannheim.korap.interfaces.db.UserDataDbIface;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * @author hanl
+ * @date 26/02/2016
+ */
+public abstract class ContextHolder {
+
+ public static final String KUSTVAKT_DB = "kustvakt_db";
+ public static final String KUSTVAKT_ENCRYPTION = "kustvakt_encryption";
+ public static final String KUSTVAKT_AUDITING = "kustvakt_auditing";
+ public static final String KUSTVAKT_CONFIG = "kustvakt_config";
+ public static final String KUSTVAKT_USERDATA = "kustvakt_userdata";
+ public static final String KUSTVAKT_RESOURCES = "kustvakt_resources";
+
+ public static final String KUSTVAKT_AUTHENTICATION_MANAGER = "kustvakt_authenticationmanager";
+ public static final String KUSTVAKT_AUTHPROVIDERS = "kustvakt_authproviders";
+ public static final String KUSTVAKT_USERDB = "kustvakt_userdb";
+ public static final String KUSTVAKT_ADMINDB = "kustvakt_admindb";
+ public static final String KUSTVAKT_POLICIES = "kustvakt_policies";
+
+ private ApplicationContext context = null;
+ private DefaultHandler handler;
+
+
+ public ContextHolder (ApplicationContext context) {
+ this.handler = new DefaultHandler();
+ this.context = context;
+ // todo: better method?!
+ new CoreResponseHandler(getAuditingProvider());
+ }
+
+
+ protected <T> T getBean (Class<T> clazz) {
+ if (this.context != null) {
+ try {
+ return context.getBean(clazz);
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ // do nothing
+ }
+ }
+ return this.handler.getDefault(clazz);
+ }
+
+
+ protected <T> T getBean (String name) {
+ T bean = null;
+ if (this.context != null) {
+ try {
+ bean = (T) context.getBean(name);
+ }
+ catch (NoSuchBeanDefinitionException e) {
+ // do nothing
+ bean = (T) this.handler.getDefault(name);
+ }
+ }
+
+ return bean;
+ }
+
+
+ public AuditingIface getAuditingProvider () {
+ return (AuditingIface) getBean(KUSTVAKT_AUDITING);
+ }
+
+
+ @Deprecated
+ public <T extends KustvaktConfiguration> T getConfiguration () {
+ return (T) getBean(KUSTVAKT_CONFIG);
+ }
+
+
+ @Deprecated
+ public PersistenceClient getPersistenceClient () {
+ return getBean(KUSTVAKT_DB);
+ }
+
+ @Deprecated
+ public Collection<UserDataDbIface> getUserDataProviders () {
+ return getBean(KUSTVAKT_USERDATA);
+ }
+
+ private void close () {
+ this.getAuditingProvider().finish();
+ this.context = null;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java b/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
new file mode 100644
index 0000000..982985f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/DefaultHandler.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.config;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 17/06/2015
+ */
+public class DefaultHandler {
+
+ private Map<String, Object> defaults;
+
+
+ public DefaultHandler () {
+ this.defaults = new HashMap<>();
+ loadClasses();
+ }
+
+
+ private void loadClasses () {
+ Set<Class<?>> cls = KustvaktClassLoader
+ .loadFromAnnotation(Configurable.class);
+ for (Class clazz : cls) {
+ Configurable c = (Configurable) clazz
+ .getAnnotation(Configurable.class);
+ try {
+ this.defaults.put(c.value(), clazz.newInstance());
+ }
+ catch (InstantiationException | IllegalAccessException e) {
+ throw new RuntimeException("Could not instantiate class");
+ }
+ }
+ }
+
+
+ public Object getDefault (String name) {
+ return this.defaults.get(name);
+ }
+
+
+ public <T> T getDefault (Class<T> tClass) {
+ for (Object o : this.defaults.values()) {
+ if (o.getClass().equals(tClass))
+ return (T) o;
+ }
+ return null;
+ }
+
+
+ public void remove (String name) {
+ this.defaults.remove(name);
+ }
+
+
+ @Override
+ public String toString () {
+ return defaults.toString();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java
new file mode 100644
index 0000000..b22cb70
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktCacheable.java
@@ -0,0 +1,118 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.InputStream;
+import java.util.Map;
+
+import de.ids_mannheim.korap.utils.ServiceInfo;
+import de.ids_mannheim.korap.utils.StringUtils;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Element;
+import net.sf.ehcache.config.CacheConfiguration;
+import net.sf.ehcache.config.PersistenceConfiguration;
+import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
+
+/**
+ * @author hanl
+ * @date 03/02/2016
+ */
+public abstract class KustvaktCacheable {
+
+ private static boolean loaded = false;
+ private String prefix;
+ private String name;
+
+ public KustvaktCacheable(String cache_name, String prefix) {
+ init();
+ if(!enabled())
+ createDefaultFileCache(cache_name);
+ this.prefix = prefix;
+ this.name = cache_name;
+ }
+
+ public KustvaktCacheable () {
+ // TODO Auto-generated constructor stub
+ }
+
+ private static Cache getCache(String name) {
+ return CacheManager.getInstance().getCache(name);
+ }
+
+
+ private void createDefaultFileCache(String name) {
+ Cache default_cache = new Cache(
+ new CacheConfiguration(name, 20000)
+ .memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LFU)
+ .eternal(false)
+ .timeToLiveSeconds(15000)
+ .timeToIdleSeconds(5000)
+ .diskExpiryThreadIntervalSeconds(0)
+ .persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.LOCALTEMPSWAP)));
+ if (!CacheManager.getInstance().cacheExists(name))
+ CacheManager.getInstance().addCache(default_cache);
+ }
+
+
+ public void init () {
+ if (!loaded) {
+ if (ServiceInfo.getInfo().getCacheable()) {
+ String file = "ehcache.xml";
+ InputStream in = ConfigLoader.loadConfigStream(file);
+ CacheManager.newInstance(in);
+ loaded = true;
+ } else {
+ CacheManager.create();
+ }
+ }
+ }
+
+ public boolean hasCacheEntry(Object key) {
+ return getCache(this.name).isKeyInCache(createKey(key.toString()));
+ }
+
+
+ public boolean enabled() {
+ // check that caching is enabled
+ return ServiceInfo.getInfo().getCacheable();
+ }
+
+ public Object getCacheValue(Object key) {
+ Element e = getCache(this.name).get(createKey(key.toString()));
+ if (e!= null)
+ return e.getObjectValue();
+ return null;
+ }
+
+ public long getCacheCreationTime(Object key) {
+ Element e = getCache(this.name).get(createKey(key.toString()));
+ if (e!= null)
+ return e.getCreationTime();
+ return -1;
+ }
+
+ public void storeInCache(Object key, Object value) {
+ getCache(this.name).put(new Element(createKey(key.toString()), value));
+ }
+
+ public void removeCacheEntry(Object key) {
+ getCache(this.name).remove(createKey(key.toString()));
+ }
+
+ public void clearCache() {
+ Cache c = getCache(this.name);
+ if (enabled()) {
+ c.removeAll();
+// c.clearStatistics();
+
+ }
+ }
+
+ private String createKey(String input) {
+ return StringUtils.toSHAHash(this.prefix+ "@" + input);
+ }
+
+ public Map<Object, Element> getAllCacheElements () {
+ Cache cache = getCache(name);
+ return cache.getAll(cache.getKeysWithExpiryCheck());
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
new file mode 100644
index 0000000..83100bc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktClassLoader.java
@@ -0,0 +1,55 @@
+package de.ids_mannheim.korap.config;
+
+import org.reflections.Reflections;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 10/06/2015
+ */
+public class KustvaktClassLoader {
+
+ private static final Reflections reflections = new Reflections(
+ "de.ids_mannheim.korap");
+
+
+ private KustvaktClassLoader () {}
+
+
+ /**
+ * loads interface implementations in current classpath
+ *
+ * @param iface
+ * @param <T>
+ * @return
+ */
+ public static <T> Set<Class<? extends T>> loadSubTypes (Class<T> iface) {
+ return reflections.getSubTypesOf(iface);
+ }
+
+
+ public static Set<Class<?>> loadFromAnnotation (
+ Class<? extends Annotation> annotation) {
+ return reflections.getTypesAnnotatedWith(annotation);
+ }
+
+
+ public static <T> Class<? extends T> getTypeClass (Class type,
+ Class<T> iface) {
+ Set<Class<? extends T>> c = KustvaktClassLoader.loadSubTypes(iface);
+ for (Class<? extends T> o : c) {
+ Type ctype = o.getGenericInterfaces()[0];
+ if (ctype instanceof ParameterizedType) {
+ ParameterizedType ptype = (ParameterizedType) ctype;
+ Class tclass = (Class) ptype.getActualTypeArguments()[0];
+ if (tclass.equals(type))
+ return o;
+ }
+ }
+ return null;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
new file mode 100644
index 0000000..40073b8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/KustvaktConfiguration.java
@@ -0,0 +1,277 @@
+package de.ids_mannheim.korap.config;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import de.ids_mannheim.korap.util.KrillProperties;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes configuration for Kustvakt by importing properties
+ * from kustvakt.conf file and setting default values if they are
+ * not configured.
+ *
+ * MH: if configuration class is extended, loadSubTypes method should be
+ * overriden
+ *
+ * @author hanl
+ * @author margaretha
+ */
+
+@Setter
+@Getter
+public class KustvaktConfiguration {
+
+ public static final Map<String, Object> KUSTVAKT_USER = new HashMap<>();
+ public static final String DATA_FOLDER = "data";
+
+ private String vcInCaching;
+
+ private String indexDir;
+ private int port;
+ // todo: make exclusive so that the containg languages can really
+ // only be used then
+ private List<String> queryLanguages;
+
+ private String serverHost;
+
+ private int maxhits;
+ private int returnhits;
+ private String keystoreLocation;
+ private String keystorePassword;
+ private Properties mailProperties;
+ private String host;
+ private String shibUserMapping;
+ private String userConfig;
+ private int inactiveTime;
+ private int loginAttemptTTL;
+ private long loginAttemptNum;
+ private boolean allowMultiLogIn;
+ private int loadFactor;
+ @Deprecated
+ private int validationStringLength;
+ @Deprecated
+ private int validationEmaillength;
+
+ private byte[] sharedSecret;
+ private int longTokenTTL;
+ private int tokenTTL;
+ private int shortTokenTTL;
+ private String[] rewrite_strategies;
+
+ private String default_pos;
+ private String default_morphology;
+ private String default_lemma;
+ private String default_orthography;
+ private String default_dep;
+ private String default_const;
+ private String apiWelcomeMessage;
+ private String defaultStructureFoundry;
+ private ArrayList<String> foundries;
+ private ArrayList<String> layers;
+
+ private String baseURL;
+ private Properties properties;
+
+ private Set<String> supportedVersions;
+ private String currentVersion;
+
+ // deprec?!
+ private final BACKENDS DEFAULT_ENGINE = BACKENDS.LUCENE;
+ private String networkEndpointURL;
+
+ // license patterns
+ protected Pattern publicLicensePattern;
+ protected Pattern freeLicensePattern;
+ protected Pattern allLicensePattern;
+
+ // random code generator
+ private String secureRandomAlgorithm;
+ private String messageDigestAlgorithm;
+
+ // EM: metadata restriction
+ // another variable might be needed to define which metadata fields are restricted
+ private boolean isMetadataRestricted = false;
+
+ // EM: Maybe needed when we support pipe registration
+ @Deprecated
+ public static Map<String, String> pipes = new HashMap<>();
+
+ public KustvaktConfiguration (Properties properties) throws Exception {
+ load(properties);
+// readPipesFile("pipes");
+ KrillProperties.setProp(properties);
+ }
+
+ public KustvaktConfiguration () {}
+
+ public void loadBasicProperties (Properties properties) {
+ port = Integer.valueOf(properties.getProperty("server.port", "8095"));
+ baseURL = properties.getProperty("kustvakt.base.url", "/api/*");
+ setSecureRandomAlgorithm(properties
+ .getProperty("security.secure.random.algorithm", ""));
+ setMessageDigestAlgorithm(
+ properties.getProperty("security.md.algorithm", "MD5"));
+ }
+
+ /**
+ * loading of the properties and mapping to parameter variables
+ *
+ * @param properties
+ * @return
+ * @throws Exception
+ */
+ protected void load (Properties properties) throws Exception {
+ loadBasicProperties(properties);
+
+ apiWelcomeMessage = properties.getProperty("api.welcome.message", "Welcome to KorAP API!");
+ currentVersion = properties.getProperty("current.api.version", "v1.0");
+
+ String supportedVersions =
+ properties.getProperty("supported.api.versions", "");
+
+ this.supportedVersions = new HashSet<>();
+ if (!supportedVersions.isEmpty()){
+ List<String> versionArray = Arrays.asList(supportedVersions.split(" "));
+ this.supportedVersions.addAll(versionArray);
+ }
+ this.supportedVersions.add(currentVersion);
+
+ maxhits = Integer.valueOf(properties.getProperty("maxhits", "50000"));
+ returnhits = Integer.valueOf(properties.getProperty("returnhits", "50000"));
+ indexDir = properties.getProperty("krill.indexDir", "");
+
+ // server options
+ serverHost = String
+ .valueOf(properties.getProperty("server.host", "localhost"));
+ String queries = properties.getProperty("korap.ql", "");
+ String[] qls = queries.split(",");
+ queryLanguages = new ArrayList<>();
+ for (String querylang : qls)
+ queryLanguages.add(querylang.trim().toUpperCase());
+
+ default_const =
+ properties.getProperty("default.foundry.constituent", "corenlp");
+ default_dep =
+ properties.getProperty("default.foundry.dependency", "malt");
+ default_lemma = properties.getProperty("default.foundry.lemma", "tt");
+ default_morphology = properties.getProperty("default.foundry.morphology", "marmot");
+ default_pos =
+ properties.getProperty("default.foundry.partOfSpeech", "tt");
+ default_orthography =
+ properties.getProperty("default.foundry.orthography", "opennlp");
+ defaultStructureFoundry =
+ properties.getProperty("default.foundry.structure", "base");
+
+ // security configuration
+ inactiveTime = TimeUtils.convertTimeToSeconds(
+ properties.getProperty("security.idleTimeoutDuration", "10M"));
+ allowMultiLogIn = Boolean
+ .valueOf(properties.getProperty("security.multipleLogIn"));
+
+ loginAttemptNum = Long.parseLong(
+ properties.getProperty("security.loginAttemptNum", "3"));
+ loginAttemptTTL = TimeUtils.convertTimeToSeconds(
+ properties.getProperty("security.authAttemptTTL", "30M"));
+
+ loadFactor = Integer.valueOf(
+ properties.getProperty("security.encryption.loadFactor", "15"));
+ validationStringLength = Integer.valueOf(properties
+ .getProperty("security.validation.stringLength", "150"));
+ validationEmaillength = Integer.valueOf(properties
+ .getProperty("security.validation.emailLength", "40"));
+
+ sharedSecret =
+ properties.getProperty("security.sharedSecret", "").getBytes();
+
+ longTokenTTL = TimeUtils.convertTimeToSeconds(
+ properties.getProperty("security.longTokenTTL", "100D"));
+ tokenTTL = TimeUtils.convertTimeToSeconds(
+ properties.getProperty("security.tokenTTL", "72H"));
+ shortTokenTTL = TimeUtils.convertTimeToSeconds(
+ properties.getProperty("security.shortTokenTTL", "3H"));
+
+ // network endpoint
+ networkEndpointURL =
+ properties.getProperty("network.endpoint.url", "");
+ }
+
+ @Deprecated
+ public void readPipesFile (String filename) throws IOException {
+ File file = new File(filename);
+ if (file.exists()) {
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader(new FileInputStream(file)));
+
+ String line = null;
+ while( (line=br.readLine())!=null ){
+ String[] parts = line.split("\t");
+ if (parts.length !=2){
+ continue;
+ }
+ else{
+ pipes.put(parts[0], parts[1]);
+ }
+ }
+ br.close();
+ }
+ }
+
+
+ /**
+ * set properties
+ *
+ * @param props
+ * @throws IOException
+ */
+ // public void setProperties (Properties props) throws IOException
+ // {
+ // this.load(props);
+ // }
+
+ /**
+ * properties can be overloaded after spring init
+ *
+ * @param stream
+ * @throws Exception
+ */
+ public void setPropertiesAsStream (InputStream stream) throws Exception {
+ try {
+
+ Properties p = new Properties();
+ p.load(stream);
+ this.load(p);
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ public BACKENDS chooseBackend (String value) {
+ if (value == null || value.equals("null"))
+ return DEFAULT_ENGINE;
+ else
+ return Enum.valueOf(BACKENDS.class, value.toUpperCase());
+ }
+
+ public enum BACKENDS {
+ NEO4J, LUCENE, NETWORK
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java b/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
new file mode 100644
index 0000000..2d9a255
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/ParamFields.java
@@ -0,0 +1,44 @@
+package de.ids_mannheim.korap.config;
+
+import lombok.Getter;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * @author hanl
+ * @date 21/07/2015
+ */
+// could also be an array or list!
+public class ParamFields extends HashMap<String, ParamFields.Param> {
+
+ public void add (Param param) {
+ this.put(param.getClass().getName(), param);
+ }
+
+
+ public <T extends Param> T get (Class<T> cl) {
+ return (T) this.get(cl.getName());
+ }
+
+
+ public <T extends Param> T remove (Class<T> cl) {
+ return (T) this.remove(cl.getName());
+ }
+
+
+ public void addAll (Collection<Param> params) {
+ for (Param p : params)
+ super.put(p.getClass().getName(), p);
+ }
+
+ @Getter
+ public abstract static class Param {
+
+ public boolean hasValues () {
+ return false;
+ }
+
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java b/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java
new file mode 100644
index 0000000..bc21668
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/QueryBuilderUtil.java
@@ -0,0 +1,21 @@
+package de.ids_mannheim.korap.config;
+
+import de.ids_mannheim.korap.query.serialize.MetaQueryBuilder;
+
+/**
+ * @author hanl
+ * @date 25/06/2015
+ */
+public class QueryBuilderUtil {
+
+ public static MetaQueryBuilder defaultMetaBuilder (Integer pageIndex,
+ Integer pageInteger, Integer pageLength, String ctx, Boolean cutoff) {
+ MetaQueryBuilder meta = new MetaQueryBuilder();
+ meta.addEntry("startIndex", pageIndex)
+ .addEntry("startPage", pageInteger)
+ .addEntry("count", pageLength).setSpanContext(ctx)
+ .addEntry("cutOff", cutoff);
+ return meta;
+
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java b/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java
new file mode 100644
index 0000000..20e5627
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/Scopes.java
@@ -0,0 +1,95 @@
+package de.ids_mannheim.korap.config;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.Userdata;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 09/12/2014
+ */
+public class Scopes {
+
+ public enum Scope {
+ openid, profile, email, queries, account, preferences, search
+
+ }
+
+ private static final String[] profile = { Attributes.EMAIL,
+ Attributes.FIRSTNAME, Attributes.LASTNAME, Attributes.INSTITUTION,
+ Attributes.ADDRESS, Attributes.PHONE, Attributes.GENDER,
+ Attributes.COUNTRY };
+
+ private static final String[] OPENID_CONNECT = { Scope.profile.toString() };
+
+ private static final Enum[] SERVICE_DEFAULTS = { Scope.account,
+ Scope.preferences, Scope.search, Scope.queries };
+
+
+ public static Scopes getProfileScopes (Userdata values) {
+ Scopes r = new Scopes();
+ for (String key : profile) {
+ Object v = values.get(key);
+ if (v != null)
+ r.values.put(key, v);
+ }
+ return r;
+ }
+
+
+ /**
+ * expects space separated values
+ *
+ * @param scopes
+ * @return
+ */
+ //todo: test
+ public static Scope[] mapScopes (String scopes) {
+ List<Enum> s = new ArrayList<>();
+ for (String value : scopes.split(" "))
+ s.add(Scope.valueOf(value.toLowerCase()));
+ return s.toArray(new Scope[s.size()]);
+ }
+
+
+ public static Scopes mapScopes (String scopes, Userdata details) {
+ Scopes m = new Scopes();
+ if (scopes != null && !scopes.isEmpty()) {
+ Scope[] scopearr = mapScopes(scopes);
+ for (Scope s : scopearr) {
+ Object v = details.get(s.toString());
+ if (v != null)
+ m.values.put(s.toString(), v);
+ }
+ if (scopes.contains(Scope.profile.toString()))
+ m.values.putAll(Scopes.getProfileScopes(details).values);
+ m.values.put(Attributes.SCOPE, scopes);
+ }
+ return m;
+ }
+
+ private Map<String, Object> values;
+
+
+ private Scopes () {
+ this.values = new HashMap<>();
+ }
+
+
+ public String toEntity () throws KustvaktException {
+ if (this.values.isEmpty())
+ return "";
+ return JsonUtils.toJSON(this.values);
+ }
+
+
+ public Map<String, Object> toMap () {
+ return new HashMap<>(this.values);
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java b/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java
new file mode 100644
index 0000000..9fd7911
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/config/URIParam.java
@@ -0,0 +1,28 @@
+package de.ids_mannheim.korap.config;
+
+import lombok.Getter;
+
+/**
+ * @author hanl
+ * @date 15/07/15
+ */
+@Getter
+public class URIParam extends ParamFields.Param {
+
+ private final String uriFragment;
+ private final Long uriExpiration;
+
+
+ public URIParam (String uri, Long expire) {
+ this.uriFragment = uri;
+ this.uriExpiration = expire;
+ }
+
+
+ @Override
+ public boolean hasValues () {
+ return this.uriFragment != null && !this.uriFragment.isEmpty()
+ && this.uriExpiration != null;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java b/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java
new file mode 100644
index 0000000..ea91e16
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AnnotationType.java
@@ -0,0 +1,13 @@
+package de.ids_mannheim.korap.constant;
+
+/** Defines various annotation types as constants
+ *
+ * @author margaretha
+ *
+ */
+public class AnnotationType {
+ public static String FOUNDRY = "foundry";
+ public static String LAYER = "layer";
+ public static String KEY = "key";
+ public static String VALUE = "value";
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java
new file mode 100644
index 0000000..cbdc07f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationMethod.java
@@ -0,0 +1,18 @@
+package de.ids_mannheim.korap.constant;
+
+/** Lists possible actual authentication methods. Multiple
+ * {@link AuthenticationScheme} may use an identical
+ * authentication method.
+ *
+ * @author margaretha
+ *
+ * @see AuthenticationScheme
+ *
+ */
+public enum AuthenticationMethod {
+ LDAP,
+ // not available
+ SHIBBOLETH, DATABASE,
+ // by pass authentication for testing
+ TEST;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java
new file mode 100644
index 0000000..543b3fd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/AuthenticationScheme.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.constant;
+
+import org.apache.commons.lang.WordUtils;
+
+/** Lists possible authentication schemes used in the Authorization header
+ * of HTTP requests.
+ *
+ * @author margaretha
+ *
+ */
+public enum AuthenticationScheme {
+ // standard http
+ BASIC, BEARER,
+ // custom
+ // SESSION, has not been supported yet
+ @Deprecated
+ API;
+
+ public String displayName () {
+ return WordUtils.capitalizeFully(name());
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java
new file mode 100644
index 0000000..5055972
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/OAuth2Scope.java
@@ -0,0 +1,60 @@
+package de.ids_mannheim.korap.constant;
+
+/** Defines all possible authorization scopes
+ *
+ * @author margaretha
+ *
+ */
+public enum OAuth2Scope {
+
+ ALL,
+ @Deprecated
+ ADMIN,
+
+ OPENID,
+ AUTHORIZE,
+
+ LIST_USER_CLIENT,
+ INSTALL_USER_CLIENT,
+ UNINSTALL_USER_CLIENT,
+
+ CLIENT_INFO,
+ REGISTER_CLIENT,
+ DEREGISTER_CLIENT,
+ RESET_CLIENT_SECRET,
+
+ SEARCH,
+ SERIALIZE_QUERY,
+ MATCH_INFO,
+
+ USER_INFO,
+
+ USER_GROUP_INFO,
+ CREATE_USER_GROUP,
+ DELETE_USER_GROUP,
+
+ DELETE_USER_GROUP_MEMBER,
+ ADD_USER_GROUP_MEMBER,
+
+ EDIT_USER_GROUP_MEMBER_ROLE,
+ ADD_USER_GROUP_MEMBER_ROLE,
+ DELETE_USER_GROUP_MEMBER_ROLE,
+
+ CREATE_VC,
+ VC_INFO,
+ EDIT_VC,
+ DELETE_VC,
+
+ SHARE_VC,
+ DELETE_VC_ACCESS,
+ VC_ACCESS_INFO,
+
+ CREATE_DEFAULT_SETTING,
+ READ_DEFAULT_SETTING,
+ DELETE_DEFAULT_SETTING;
+
+ @Override
+ public String toString () {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java b/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java
new file mode 100644
index 0000000..76bac5b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/TokenType.java
@@ -0,0 +1,26 @@
+package de.ids_mannheim.korap.constant;
+
+import org.apache.commons.lang.StringUtils;
+
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/** Defines the types of authentication tokens. Token types are used to
+ * create {@link TokenContext} and determine which authentication provider
+ * must be used to create a TokenContext and parse given tokens.
+ *
+ * @author margaretha
+ *
+ */
+public enum TokenType {
+ BASIC, API, SESSION,
+ // openid token, e.g. within oauth2 response (json body)
+ ID_TOKEN,
+ // OAuth2 access_token
+ BEARER,
+ // OAuth2 client
+ CLIENT;
+
+ public String displayName () {
+ return StringUtils.capitalize(name().toLowerCase());
+ }
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java
new file mode 100644
index 0000000..fe723fc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/Annotation.java
@@ -0,0 +1,52 @@
+package de.ids_mannheim.korap.core.entity;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes annotation tags available in the system / used in
+ * annotating corpus data.
+ *
+ * @author margaretha
+ *
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation")
+public class Annotation {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+ private String code;
+ private String type;
+ private String text;
+ private String description;
+ @Column(name = "de_description")
+ private String germanDescription;
+
+ public Annotation () {}
+
+ public Annotation (String code, String type, String text,
+ String description) {
+ this.code = code;
+ this.type = type;
+ this.text = text;
+ this.description = description;
+ }
+
+ @Override
+ public String toString () {
+ return "id=" + id + ", code= " + code + ", type= " + type
+ + ", description=" + description + ", germanDescription="
+ + germanDescription;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java
new file mode 100644
index 0000000..f94c715
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationKey.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes the annotation key mapping to annotation_key table in the
+ * database and annotation key relations to {@link AnnotationLayer}
+ * and {@link Annotation}.
+ *
+ * @author margaretha
+ *
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation_key", uniqueConstraints = @UniqueConstraint(
+ columnNames = { "layer_id", "key_id" }))
+public class AnnotationKey {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+ @Column(name = "key_id")
+ private int keyId;
+ @Column(name = "layer_id")
+ private int layerId;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "layer_id", insertable = false, updatable = false)
+ private AnnotationLayer layer;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "key_id", insertable = false, updatable = false)
+ private Annotation key;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "annotation_value",
+ joinColumns = @JoinColumn(name = "key_id",
+ referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "value_id",
+ referencedColumnName = "id"),
+ uniqueConstraints = @UniqueConstraint(
+ columnNames = { "key_id", "value_id" }))
+ private Set<Annotation> values;
+
+ public AnnotationKey () {}
+
+ public AnnotationKey (int layerId, int keyId) {
+ this.layerId = layerId;
+ this.keyId = keyId;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java
new file mode 100644
index 0000000..3160cef
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/AnnotationLayer.java
@@ -0,0 +1,68 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import org.hibernate.annotations.Fetch;
+import org.hibernate.annotations.FetchMode;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes annotation layers as a pair of foundry and layer where
+ * foundry denotes where the annotation comes from e.g. Tree tagger
+ * parser, and layer denotes the annotation layer e.g. part of speech.
+ *
+ * @author margaretha
+ * @see Annotation
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "annotation_layer", uniqueConstraints = @UniqueConstraint(
+ columnNames = { "foundry_id", "layer_id" }))
+public class AnnotationLayer {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+ @Column(name = "foundry_id")
+ private int foundryId;
+ @Column(name = "layer_id")
+ private int layerId;
+ @Column(name = "description")
+ private String description;
+
+ @Fetch(FetchMode.SELECT)
+ @ManyToOne // (fetch=FetchType.LAZY)
+ @JoinColumn(name = "foundry_id", insertable = false, updatable = false)
+ private Annotation foundry;
+
+ @Fetch(FetchMode.SELECT)
+ @ManyToOne // (fetch=FetchType.LAZY)
+ @JoinColumn(name = "layer_id", insertable = false, updatable = false)
+ private Annotation layer;
+
+ @OneToMany(mappedBy = "layer", fetch = FetchType.EAGER,
+ cascade = CascadeType.REMOVE)
+ private Set<AnnotationKey> keys;
+
+ @Override
+ public String toString () {
+ return "id=" + id + ", foundry=" + foundry + ", layer=" + layer
+ + ", description=" + description + ", keys= " + keys;
+
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java b/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java
new file mode 100644
index 0000000..1f26d7f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/entity/Resource.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.core.entity;
+
+import java.util.Set;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.Table;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes resources having free licenses. Primarily for
+ * accommodating clients in providing data without login such as
+ * KorapSRU.
+ *
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+@Entity
+@Table(name = "resource")
+public class Resource {
+
+ @Id
+ private String id;
+
+ @Column(name = "de_title")
+ private String germanTitle;
+
+ @Column(name = "en_title")
+ private String englishTitle;
+
+ @Column(name = "en_description")
+ private String englishDescription;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(name = "resource_layer",
+ joinColumns = @JoinColumn(name = "resource_id",
+ referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "layer_id",
+ referencedColumnName = "id"))
+ private Set<AnnotationLayer> layers;
+
+ public Resource () {}
+
+ public Resource (String id, String germanTitle, String englishTitle,
+ String englishDescription, Set<AnnotationLayer> layers) {
+ this.id = id;
+ this.germanTitle = germanTitle;
+ this.englishTitle = englishTitle;
+ this.englishDescription = englishDescription;
+ this.layers = layers;
+ }
+
+ @Override
+ public String toString () {
+ return "id=" + id + ", germanTitle=" + germanTitle + ", englishTitle="
+ + englishTitle + ", description=" + englishDescription
+ + ", layers= " + layers;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java
new file mode 100644
index 0000000..4730a4f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/AnnotationService.java
@@ -0,0 +1,96 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.web.controller.AnnotationController;
+import de.ids_mannheim.korap.dao.AnnotationDao;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.LayerDto;
+import de.ids_mannheim.korap.dto.converter.AnnotationConverter;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/** AnnotationService defines the logic behind {@link AnnotationController}.
+ *
+ * @author margaretha
+ *
+ */
+@Service
+public class AnnotationService {
+
+ private static final boolean DEBUG = false;
+
+ private static Logger jlog =
+ LogManager.getLogger(AnnotationService.class);
+
+ @Autowired
+ private AnnotationDao annotationDao;
+
+ @Autowired
+ private AnnotationConverter annotationConverter;
+
+ public List<LayerDto> getLayerDtos () {
+ List<AnnotationLayer> layers = annotationDao.getAllFoundryLayerPairs();
+ if (DEBUG){
+ jlog.debug("/layers " + layers.toString());
+ }
+ List<LayerDto> layerDto = annotationConverter.convertToLayerDto(layers);
+ return layerDto;
+ }
+
+ public List<FoundryDto> getFoundryDtos (List<String> codes, String language)
+ throws KustvaktException {
+ List<AnnotationLayer> annotationPairs = null;
+ String foundry = "", layer = "";
+ if (codes.contains("*")) {
+ annotationPairs =
+ annotationDao.getAnnotationDescriptions(foundry, layer);
+ }
+ else {
+ String[] annotationCode;
+ annotationPairs = new ArrayList<AnnotationLayer>();
+ for (String code : codes) {
+ if (DEBUG){
+ jlog.debug("code " + code);
+ }
+ annotationCode = code.split("/");
+ if (annotationCode.length == 1) {
+ foundry = annotationCode[0];
+ }
+ else if (annotationCode.length == 2) {
+ foundry = annotationCode[0];
+ layer = annotationCode[1];
+ }
+ else {
+ jlog.error("Annotation code is wrong: " + annotationCode);
+ throw new KustvaktException(StatusCodes.INVALID_ATTRIBUTE,
+ "Bad attribute:", code);
+ }
+
+ annotationPairs.addAll(annotationDao
+ .getAnnotationDescriptions(foundry, layer));
+ }
+ }
+
+ if (annotationPairs != null && !annotationPairs.isEmpty()) {
+ List<FoundryDto> foundryDtos = annotationConverter
+ .convertToFoundryDto(annotationPairs, language);
+ if (DEBUG){
+ jlog.debug("/description " + annotationPairs.toString());
+ }
+ return foundryDtos;
+ }
+ else {
+ throw new KustvaktException(StatusCodes.NO_RESULT_FOUND,
+ "No result found", "");
+ }
+
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
new file mode 100644
index 0000000..c1072dd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/BasicService.java
@@ -0,0 +1,17 @@
+package de.ids_mannheim.korap.core.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/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java
new file mode 100644
index 0000000..de6038e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchNetworkEndpoint.java
@@ -0,0 +1,87 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.apache.http.HttpStatus;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+@Service
+public class SearchNetworkEndpoint {
+
+ private final static Logger jlog = LogManager
+ .getLogger(SearchNetworkEndpoint.class);
+
+ @Autowired
+ private KustvaktConfiguration config;
+
+ public String search (String query) throws KustvaktException {
+ String networkEndpointURL = config.getNetworkEndpointURL();
+ if (networkEndpointURL == null || networkEndpointURL.isEmpty()) {
+ throw new KustvaktException(
+ StatusCodes.NETWORK_ENDPOINT_NOT_AVAILABLE,
+ "Network endpoint is not available");
+ }
+ else {
+ try {
+ URL url = new URL(networkEndpointURL);
+ HttpURLConnection connection = (HttpURLConnection) url
+ .openConnection();
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type",
+ "application/json; charset=UTF-8");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setDoOutput(true);
+ OutputStream os = connection.getOutputStream();
+ byte[] input = query.getBytes("utf-8");
+ os.write(input, 0, input.length);
+
+ String entity = null;
+ if (connection.getResponseCode() == HttpStatus.SC_OK) {
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader(connection.getInputStream(),
+ "utf-8"));
+ StringBuilder response = new StringBuilder();
+ String responseLine = null;
+ while ((responseLine = br.readLine()) != null) {
+ response.append(responseLine.trim());
+ }
+ entity = response.toString();
+ }
+
+ if (entity != null && !entity.isEmpty()) {
+ return entity;
+ }
+ else {
+ String message = connection.getResponseCode() + " "
+ + connection.getResponseMessage();
+ jlog.warn("Search on network endpoint failed "
+ + networkEndpointURL + ". Message: " + message);
+
+ throw new KustvaktException(
+ StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+ "Failed searching at network endpoint: "
+ + networkEndpointURL,
+ message);
+ }
+ }
+ catch (Exception e) {
+ throw new KustvaktException(
+ StatusCodes.SEARCH_NETWORK_ENDPOINT_FAILED,
+ "Failed searching at network endpoint: "
+ + networkEndpointURL,
+ e.getCause());
+ }
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
new file mode 100644
index 0000000..96b5f62
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/SearchService.java
@@ -0,0 +1,493 @@
+package de.ids_mannheim.korap.core.service;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import javax.annotation.PostConstruct;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.http.HttpStatus;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+//import de.ids_mannheim.de.init.VCLoader;
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+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.query.serialize.MetaQueryBuilder;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import de.ids_mannheim.korap.response.Notifications;
+import de.ids_mannheim.korap.rewrite.RewriteHandler;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.User.CorpusAccess;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.ClientsHandler;
+import de.ids_mannheim.korap.web.SearchKrill;
+
+@Service
+public class SearchService extends BasicService{
+
+ private static final boolean DEBUG = false;
+
+ private static Logger jlog = LogManager.getLogger(SearchService.class);
+
+ @Autowired
+ private KustvaktConfiguration config;
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private RewriteHandler rewriteHandler;
+
+ @Autowired
+ private SearchKrill searchKrill;
+ @Autowired
+ private SearchNetworkEndpoint searchNetwork;
+
+ private ClientsHandler graphDBhandler;
+
+ @PostConstruct
+ private void doPostConstruct () {
+ UriBuilder builder = UriBuilder.fromUri("http://10.0.10.13").port(9997);
+ this.graphDBhandler = new ClientsHandler(builder.build());
+ }
+
+ public String getKrillVersion () {
+ return searchKrill.getIndex().getVersion();
+
+ }
+
+ @SuppressWarnings("unchecked")
+ public String serializeQuery (String q, String ql, String v, String cq,
+ Integer pageIndex, Integer startPage, Integer pageLength,
+ String context, Boolean cutoff, boolean accessRewriteDisabled)
+ throws KustvaktException {
+ QuerySerializer ss = new QuerySerializer().setQuery(q, ql, v);
+ if (cq != null) ss.setCollection(cq);
+
+ MetaQueryBuilder meta = new MetaQueryBuilder();
+ if (pageIndex != null) meta.addEntry("startIndex", pageIndex);
+ if (pageIndex == null && startPage != null)
+ meta.addEntry("startPage", startPage);
+ if (pageLength != null) meta.addEntry("count", pageLength);
+ if (context != null) meta.setSpanContext(context);
+ meta.addEntry("cutOff", cutoff);
+
+ ss.setMeta(meta.raw());
+ // return ss.toJSON();
+
+ String query = ss.toJSON();
+ query = rewriteHandler.processQuery(ss.toJSON(), null);
+ return query;
+ }
+
+ private User createUser (String username, HttpHeaders headers)
+ throws KustvaktException {
+ User user = authenticationManager.getUser(username);
+ authenticationManager.setAccessAndLocation(user, headers);
+ if (DEBUG) {
+ if (user != null) {
+ jlog.debug("Debug: user location=" + user.locationtoString()
+ + ", access=" + user.accesstoString());
+ }
+ }
+ return user;
+ }
+
+ public String search (String jsonld, String username, HttpHeaders headers)
+ throws KustvaktException {
+
+ User user = createUser(username, headers);
+
+ JsonNode node = JsonUtils.readTree(jsonld);
+ node = node.at("/meta/snippets");
+ if (node !=null && node.asBoolean()){
+ user.setCorpusAccess(CorpusAccess.ALL);
+ }
+
+ String query = this.rewriteHandler.processQuery(jsonld, user);
+ // MH: todo: should be possible to add the meta part to
+ // the query serialization
+ // User user = controller.getUser(ctx.getUsername());
+ // jsonld = this.processor.processQuery(jsonld, user);
+ return searchKrill.search(query);
+ }
+
+ @SuppressWarnings("unchecked")
+ public String search (String engine, String username, HttpHeaders headers,
+ 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,
+ boolean showTokens, boolean showSnippet)
+ throws KustvaktException {
+
+ if (pageInteger != null && pageInteger < 1) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ "page must start from 1", "page");
+ }
+
+ String[] pipeArray = null;
+ if (pipes!=null && !pipes.isEmpty()){
+ pipeArray = pipes.split(",");
+ }
+
+ User user = createUser(username, headers);
+ CorpusAccess corpusAccess = user.getCorpusAccess();
+
+ // EM: TODO: check if requested fields are public metadata. Currently
+ // it is not needed because all metadata are public.
+ if (accessRewriteDisabled){
+ corpusAccess = CorpusAccess.ALL;
+ user.setCorpusAccess(CorpusAccess.ALL);
+ }
+
+ QuerySerializer serializer = new QuerySerializer();
+ serializer.setQuery(q, ql, v);
+ String cq = combineMultipleCorpusQuery(cqList);
+ if (cq != null) serializer.setCollection(cq);
+
+ List<String> fieldList = convertFieldsToList(fields);
+ handleNonPublicFields(fieldList, accessRewriteDisabled, serializer);
+
+ MetaQueryBuilder meta = createMetaQuery(pageIndex, pageInteger, ctx,
+ pageLength, cutoff, corpusAccess, fieldList, accessRewriteDisabled,
+ showTokens, showSnippet);
+ serializer.setMeta(meta.raw());
+
+ // There is an error in query processing
+ // - either query, corpus or meta
+ if (serializer.hasErrors()) {
+ throw new KustvaktException(serializer.toJSON());
+ }
+
+ String query = serializer.toJSON();
+
+ if (accessRewriteDisabled && showTokens) {
+ Notifications n = new Notifications();
+ n.addWarning(StatusCodes.NOT_ALLOWED,
+ "Tokens cannot be shown without access.");
+ JsonNode warning = n.toJsonNode();
+ query = addWarning(query, warning);
+ }
+
+ query = runPipes(query,pipeArray);
+
+ query = this.rewriteHandler.processQuery(query, user);
+ if (DEBUG){
+ jlog.debug("the serialized query " + query);
+ }
+
+ KustvaktConfiguration.BACKENDS searchEngine = this.config.chooseBackend(engine);
+ String result;
+ if (searchEngine.equals(KustvaktConfiguration.BACKENDS.NEO4J)) {
+ result = searchNeo4J(query, pageLength, meta, false);
+ }
+ else if (searchEngine.equals(KustvaktConfiguration.BACKENDS.NETWORK)) {
+ result = searchNetwork.search(query);
+ }
+ else {
+ result = searchKrill.search(query);
+ }
+ // jlog.debug("Query result: " + result);
+ return result;
+
+ }
+
+ /**
+ * Pipes are service URLs for modifying KoralQuery. A POST request
+ * with Content-Type application/json will be sent for each pipe.
+ * Kustvakt expects a KoralQuery in JSON format as the pipe response.
+ *
+ * @param query the original koral query
+ * @param pipeArray the pipe service URLs
+ * @param serializer the query serializer
+ * @return a modified koral query
+ * @throws KustvaktException
+ */
+ private String runPipes (String query, String[] pipeArray) throws KustvaktException {
+ if (pipeArray !=null){
+ for (int i=0; i<pipeArray.length; i++){
+ String pipeURL = pipeArray[i];
+ try {
+ URL url = new URL(pipeURL);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setDoOutput(true);
+ OutputStream os = connection.getOutputStream();
+ byte[] input = query.getBytes("utf-8");
+ os.write(input, 0, input.length);
+
+ String entity = null;
+ if (connection.getResponseCode() == HttpStatus.SC_OK) {
+ BufferedReader br =
+ new BufferedReader(new InputStreamReader(
+ connection.getInputStream(), "utf-8"));
+ StringBuilder response = new StringBuilder();
+ String responseLine = null;
+ while ((responseLine = br.readLine()) != null) {
+ response.append(responseLine.trim());
+ }
+ entity = response.toString();
+ }
+
+ if (entity != null && !entity.isEmpty()) {
+ query = entity;
+ }
+ else {
+ query = handlePipeError(query, pipeURL,
+ connection.getResponseCode() + " "
+ + connection.getResponseMessage());
+ }
+ }
+ catch (Exception e) {
+ query = handlePipeError(query, pipeURL,
+ e.getMessage());
+ }
+ }
+ }
+ return query;
+ }
+
+ private String handlePipeError (String query, String url,
+ String message) throws KustvaktException {
+ jlog.warn("Failed running the pipe at " + url + ". Message: "+ message);
+
+ Notifications n = new Notifications();
+ n.addWarning(StatusCodes.PIPE_FAILED,
+ "Pipe failed", url, message);
+ JsonNode warning = n.toJsonNode();
+
+ query = addWarning(query, warning);
+ return query;
+ }
+
+
+ private String addWarning (String query, JsonNode warning)
+ throws KustvaktException {
+
+ ObjectNode node = (ObjectNode) JsonUtils.readTree(query);
+ if (node.has("warnings")){
+ warning = warning.at("/warnings/0");
+ ArrayNode arrayNode = (ArrayNode) node.get("warnings");
+ arrayNode.add(warning);
+ node.set("warnings", arrayNode);
+ }
+ else{
+ node.setAll((ObjectNode) warning);
+ }
+ return node.toString();
+ }
+
+ private void handleNonPublicFields (List<String> fieldList,
+ boolean accessRewriteDisabled, QuerySerializer serializer) {
+ List<String> nonPublicFields = new ArrayList<>();
+ nonPublicFields.add("snippet");
+
+ List<String> ignoredFields = new ArrayList<>();
+ if (accessRewriteDisabled && !fieldList.isEmpty()) {
+ for (String field : fieldList) {
+ if (nonPublicFields.contains(field)) {
+ ignoredFields.add(field);
+ }
+ }
+ if (!ignoredFields.isEmpty()) {
+ serializer.addWarning(StatusCodes.NON_PUBLIC_FIELD_IGNORED,
+ "The requested non public fields are ignored",
+ ignoredFields);
+ }
+ }
+ }
+
+ private MetaQueryBuilder createMetaQuery (Integer pageIndex,
+ Integer pageInteger, String ctx, Integer pageLength,
+ Boolean cutoff, CorpusAccess corpusAccess, List<String> fieldList,
+ boolean accessRewriteDisabled,
+ boolean showTokens, boolean showSnippet) {
+ MetaQueryBuilder meta = new MetaQueryBuilder();
+ meta.addEntry("startIndex", pageIndex);
+ meta.addEntry("startPage", pageInteger);
+ meta.setSpanContext(ctx);
+ meta.addEntry("count", pageLength);
+ // todo: what happened to cutoff?
+ meta.addEntry("cutOff", cutoff);
+ meta.addEntry("snippets", (showSnippet && !accessRewriteDisabled));
+ if (!accessRewriteDisabled) {
+ meta.addEntry("tokens", showTokens);
+ }
+
+ // meta.addMeta(pageIndex, pageInteger, pageLength, ctx,
+ // cutoff);
+ // fixme: should only apply to CQL queries per default!
+ // meta.addEntry("itemsPerResource", 1);
+
+ if (corpusAccess.equals(CorpusAccess.FREE)){
+ meta.addEntry("timeout", 10000);
+ }
+ else{
+ meta.addEntry("timeout", 90000);
+ }
+
+ if (fieldList != null && !fieldList.isEmpty()){
+ meta.addEntry("fields", fieldList);
+ }
+ return meta;
+ }
+
+ private List<String> convertFieldsToList (String fields) {
+ if (fields != null && !fields.isEmpty()) {
+ String[] fieldArray = fields.split(",");
+ List<String> fieldList = new ArrayList<>(fieldArray.length);
+ for (String field : fieldArray) {
+ fieldList.add(field.trim());
+ }
+ return fieldList;
+ }
+ else {
+ return new ArrayList<>();
+ }
+ }
+
+ private String searchNeo4J (String query, int pageLength,
+ MetaQueryBuilder meta, boolean raw) throws KustvaktException {
+
+ if (raw) {
+ throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+ "raw not supported!");
+ }
+
+ MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
+ map.add("q", query);
+ map.add("count", String.valueOf(pageLength));
+ map.add("lctxs", String.valueOf(meta.getSpanContext().getLeftSize()));
+ map.add("rctxs", String.valueOf(meta.getSpanContext().getRightSize()));
+ return this.graphDBhandler.getResponse(map, "distKwic");
+
+ }
+
+ private Pattern determineAvailabilityPattern (User user) {
+ Pattern p = null;
+ if (user != null) {
+ CorpusAccess corpusAccess = user.getCorpusAccess();
+ switch (corpusAccess) {
+ case PUB:
+ p = config.getPublicLicensePattern();
+ break;
+ case ALL:
+ p = config.getAllLicensePattern();
+ break;
+ default: // FREE
+ p = config.getFreeLicensePattern();
+ break;
+ }
+ }
+ return p;
+ }
+
+ public String retrieveMatchInfo (
+ String corpusId, String docId,
+ String textId, String matchId, boolean info, Set<String> foundries,
+ String username, HttpHeaders headers, Set<String> layers,
+ boolean spans,
+ boolean snippet, boolean tokens,
+ boolean sentenceExpansion,
+ boolean highlights
+ ) throws KustvaktException {
+ String matchid =
+ searchKrill.getMatchId(corpusId, docId, textId, matchId);
+
+ User user = createUser(username, headers);
+ Pattern p = determineAvailabilityPattern(user);
+
+// boolean match_only = foundries == null || foundries.isEmpty();
+ String results;
+// try {
+
+ ArrayList<String> foundryList = null;
+ ArrayList<String> layerList = null;
+
+ if (foundries != null && !foundries.isEmpty()) {
+ foundryList = new ArrayList<String>();
+ layerList = new ArrayList<String>();
+ // EM: now without user, just list all foundries and
+ // layers
+ if (foundries.contains("*")) {
+ foundryList = config.getFoundries();
+ layerList = config.getLayers();
+ }
+ else {
+ foundryList.addAll(foundries);
+ layerList.addAll(layers);
+ }
+ } else {
+ sentenceExpansion = false;
+ spans = false;
+ info = false;
+ highlights = true;
+ };
+
+ results = searchKrill.getMatch(
+ matchid, info, foundryList, layerList,
+ spans, snippet, tokens, highlights,
+ sentenceExpansion, p);
+// }
+// catch (Exception e) {
+// jlog.error("Exception in the MatchInfo service encountered!", e);
+// throw new KustvaktException(StatusCodes.ILLEGAL_ARGUMENT,
+// e.getMessage());
+// }
+ if (DEBUG){
+ jlog.debug("MatchInfo results: " + results);
+ }
+ return results;
+ }
+
+ public String retrieveDocMetadata (String corpusId, String docId,
+ String textId, String fields, String username, HttpHeaders headers)
+ throws KustvaktException {
+ List<String> fieldList = null;
+ if (fields != null && !fields.isEmpty()){
+ fieldList = convertFieldsToList(fields);
+ }
+ Pattern p = null;
+ if (config.isMetadataRestricted()){
+ User user = createUser(username, headers);
+ p = determineAvailabilityPattern(user);
+ }
+ String textSigle = searchKrill.getTextSigle(corpusId, docId, textId);
+ return searchKrill.getFields(textSigle, fieldList, p);
+ }
+
+ public String getCollocationBase (String query) throws KustvaktException {
+ return graphDBhandler.getResponse("distCollo", "q", query);
+ }
+
+ public void closeIndexReader () throws KustvaktException {
+ searchKrill.closeIndexReader();
+ }
+
+ /**
+ * Return the fingerprint of the latest index revision.
+ */
+ public String getIndexFingerprint () {
+ return searchKrill.getIndexFingerprint();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java b/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
new file mode 100644
index 0000000..54f5e1a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/service/StatisticService.java
@@ -0,0 +1,105 @@
+package de.ids_mannheim.korap.core.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 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;
+ }
+
+ /**
+ * Return the fingerprint of the latest index revision.
+ */
+ public String getIndexFingerprint () {
+ return searchKrill.getIndexFingerprint();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java
new file mode 100644
index 0000000..26d51dc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/AnnotationController.java
@@ -0,0 +1,125 @@
+package de.ids_mannheim.korap.core.web.controller;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.core.service.AnnotationService;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.LayerDto;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ * Provides services regarding annotation related information.
+ *
+ * @author margaretha
+ *
+ */
+@Controller
+@Path("/{version}/annotation/")
+@ResourceFilters({APIVersionFilter.class, DemoUserFilter.class, PiwikFilter.class })
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+public class AnnotationController {
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Autowired
+ private AnnotationService annotationService;
+
+ /**
+ * Returns information about all supported layers
+ *
+ * @return a json serialization of all supported layers
+ */
+ @GET
+ @Path("layers")
+ public List<LayerDto> getLayers () {
+ return annotationService.getLayerDtos();
+ }
+
+
+ /**
+ * Returns a list of foundry descriptions.
+ *
+ * @param codes
+ * foundry-layer code or a Kleene-star
+ * @param language
+ * 2-letter language code (description language)
+ * @return a list of foundry, layer, value information in json
+ */
+ @SuppressWarnings("unchecked")
+ @POST
+ @Path("description")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public List<FoundryDto> getFoundryDescriptions (String json) {
+ if (json == null || json.isEmpty()) {
+ throw kustvaktResponseHandler
+ .throwit(new KustvaktException(StatusCodes.MISSING_PARAMETER,
+ "Missing a json string.", ""));
+ }
+
+ JsonNode node;
+ try {
+ node = JsonUtils.readTree(json);
+ }
+ catch (KustvaktException e1) {
+ throw kustvaktResponseHandler.throwit(e1);
+ }
+
+ String language;
+ if (!node.has("language")) {
+ language = "en";
+ }
+ else {
+ language = node.get("language").asText();
+ if (language == null || language.isEmpty()) {
+ language = "en";
+ }
+ else if (!(language.equals("en") || language.equals("de"))) {
+ throw kustvaktResponseHandler.throwit(
+ new KustvaktException(StatusCodes.UNSUPPORTED_VALUE,
+ "Unsupported value:", language));
+ }
+ }
+
+ List<String> codes;
+ try {
+ codes = JsonUtils.convert(node.get("codes"), List.class);
+ }
+ catch (IOException | NullPointerException e) {
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.INVALID_ARGUMENT, "Bad argument:", json));
+ }
+ if (codes == null || codes.isEmpty()) {
+ throw kustvaktResponseHandler.throwit(
+ new KustvaktException(StatusCodes.MISSING_ATTRIBUTE,
+ "codes is null or empty", "codes"));
+ }
+
+ try {
+ return annotationService.getFoundryDtos(codes, language);
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+}
+
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
new file mode 100644
index 0000000..acddf23
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/SearchController.java
@@ -0,0 +1,490 @@
+package de.ids_mannheim.korap.core.web.controller;// package
+ // de.ids_mannheim.korap.ext.web;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.web.utils.SearchResourceFilters;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.constant.OAuth2Scope;
+import de.ids_mannheim.korap.core.service.SearchService;
+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.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.ServiceInfo;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.AdminFilter;
+import de.ids_mannheim.korap.web.filter.AuthenticationFilter;
+import de.ids_mannheim.korap.web.filter.DemoUserFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ *
+ * @author hanl, margaretha, diewald
+ * @date 29/01/2014
+ * @lastUpdate 05/07/2019
+ *
+ */
+@Controller
+@Path("/")
+@ResourceFilters({ APIVersionFilter.class, AuthenticationFilter.class,
+ DemoUserFilter.class, PiwikFilter.class })
+public class SearchController {
+
+ private static final boolean DEBUG = false;
+
+ private static Logger jlog = LogManager.getLogger(SearchController.class);
+ private @Context ServletContext context;
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Autowired
+ private SearchService searchService;
+ @Autowired
+ private OAuth2ScopeService scopeService;
+ @Autowired
+ private KustvaktConfiguration config;
+
+ @GET
+ @Path("{version}")
+ public Response index (){
+ return Response
+ .ok(config.getApiWelcomeMessage())
+ .header("X-Index-Revision", searchService.getIndexFingerprint())
+ .build();
+ }
+
+ @GET
+ @Path("{version}/info")
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public Response info (){
+ Map<String, Object> m = new HashMap<>();
+ m.put("latest_api_version", config.getCurrentVersion());
+ m.put("supported_api_versions", config.getSupportedVersions());
+ m.put("kustvakt_version", ServiceInfo.getInfo().getVersion());
+ m.put("krill_version", searchService.getKrillVersion());
+ m.put("koral_version", ServiceInfo.getInfo().getKoralVersion());
+ try {
+ return Response.ok(JsonUtils.toJSON(m)).build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
+ @POST
+ @Path("{version}/index/close")
+ // overrides the whole filters
+ @ResourceFilters({APIVersionFilter.class,AdminFilter.class})
+ public Response closeIndexReader (){
+ try {
+ searchService.closeIndexReader();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ return Response.ok().build();
+ }
+
+
+// EM: This web service is DISABLED until there is a need for it.
+// ND: In case rewrite is supported, it could be used to check the authorization
+// scope without searching etc. In case not, it helps to compare queries in
+// different query languages.
+// MH: ref query parameter removed!
+// @GET
+// @Path("{version}/query")
+// @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public Response serializeQuery (@Context Locale locale,
+ @Context SecurityContext securityContext, @QueryParam("q") String q,
+ @QueryParam("ql") String ql, @QueryParam("v") String v,
+ @QueryParam("context") String context,
+ @QueryParam("cutoff") Boolean cutoff,
+ @QueryParam("count") Integer pageLength,
+ @QueryParam("offset") Integer pageIndex,
+ @QueryParam("page") Integer startPage,
+ @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
+ @QueryParam("cq") String cq) {
+ TokenContext ctx = (TokenContext) securityContext.getUserPrincipal();
+ try {
+ scopeService.verifyScope(ctx, OAuth2Scope.SERIALIZE_QUERY);
+ String result = searchService.serializeQuery(q, ql, v, cq,
+ pageIndex, startPage, pageLength, context, cutoff,
+ accessRewriteDisabled);
+ if (DEBUG){
+ jlog.debug("Query: " + result);
+ }
+ return Response.ok(result).build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
+
+// This web service is DISABLED until there is a need for it.
+ @POST
+ @Path("{version}/search")
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @SearchResourceFilters
+ public Response searchPost (@Context SecurityContext context,
+ @Context Locale locale,
+ @Context HttpHeaders headers,
+ String jsonld) {
+
+ if (DEBUG){
+ jlog.debug("Serialized search: " + jsonld);
+ }
+
+ TokenContext ctx = (TokenContext) context.getUserPrincipal();
+ try {
+ scopeService.verifyScope(ctx, OAuth2Scope.SEARCH);
+ String result = searchService.search(jsonld, ctx.getUsername(),
+ headers);
+ return Response.ok(result).build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
+ /** Performs for the given query
+ *
+ * @param securityContext
+ * @param request
+ * @param headers
+ * @param locale
+ * @param q
+ * query
+ * @param ql
+ * query language
+ * @param v
+ * query language version
+ * @param ctx
+ * result context
+ * @param cutoff
+ * determines to limit search results to one page only
+ * or not (default false)
+ * @param pageLength
+ * the number of results should be included in a page
+ * @param pageIndex
+ * @param pageInteger page number
+ * @param fields
+ * metadata fields to be included, separated by comma
+ * @param pipes
+ * external plugins for additional processing,
+ * separated by comma
+ * @param accessRewriteDisabled
+ * determine if access rewrite should be disabled
+ * (default false)
+ * @param cq
+ * corpus query defining a virtual corpus
+ * @param engine
+ * @return search results in JSON
+ */
+ @GET
+ @Path("{version}/search")
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @SearchResourceFilters
+ public Response searchGet (@Context SecurityContext securityContext,
+ @Context HttpServletRequest request,
+ @Context HttpHeaders headers, @Context Locale locale,
+ @QueryParam("q") String q, @QueryParam("ql") String ql,
+ @QueryParam("v") String v, @QueryParam("context") String ctx,
+ @QueryParam("cutoff") Boolean cutoff,
+ @QueryParam("count") Integer pageLength,
+ @QueryParam("offset") Integer pageIndex,
+ @QueryParam("page") Integer pageInteger,
+ @QueryParam("fields") String fields,
+ @QueryParam("pipes") String pipes,
+ @QueryParam("access-rewrite-disabled") boolean accessRewriteDisabled,
+ @QueryParam("show-tokens") boolean showTokens,
+ @DefaultValue("true") @QueryParam("show-snippet") boolean showSnippet,
+ @QueryParam("cq") List<String> cq,
+ @QueryParam("engine") String engine) {
+
+ TokenContext context =
+ (TokenContext) securityContext.getUserPrincipal();
+
+ String result;
+ try {
+ scopeService.verifyScope(context, OAuth2Scope.SEARCH);
+ result = searchService.search(engine, context.getUsername(),
+ headers, q, ql, v, cq, fields, pipes, pageIndex,
+ pageInteger, ctx, pageLength, cutoff,
+ accessRewriteDisabled, showTokens, showSnippet);
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+
+ return Response.ok(result).build();
+ }
+
+ // EM: legacy support
+ @Deprecated
+ @GET
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}/matchInfo")
+ @SearchResourceFilters
+ public Response getMatchInfo (@Context SecurityContext ctx,
+ @Context HttpHeaders headers, @Context Locale locale,
+ @PathParam("corpusId") String corpusId,
+ @PathParam("docId") String docId,
+ @PathParam("textId") String textId,
+ @PathParam("matchId") String matchId,
+ @QueryParam("foundry") Set<String> foundries,
+ @QueryParam("layer") Set<String> layers,
+ @QueryParam("spans") Boolean spans,
+ // Highlights may also be a list of valid highlight classes
+ @QueryParam("hls") Boolean highlights) throws KustvaktException {
+
+ return retrieveMatchInfo(ctx, headers, locale, corpusId, docId, textId,
+ matchId, foundries, layers, spans, "true", "false",
+ "sentence", highlights);
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @Path("{version}/corpus/{corpusId}/{docId}/{textId}/{matchId}")
+ @SearchResourceFilters
+ public Response retrieveMatchInfo (@Context SecurityContext ctx,
+ @Context HttpHeaders headers, @Context Locale locale,
+ @PathParam("corpusId") String corpusId,
+ @PathParam("docId") String docId,
+ @PathParam("textId") String textId,
+ @PathParam("matchId") String matchId,
+ @QueryParam("foundry") Set<String> foundries,
+ @QueryParam("layer") Set<String> layers,
+ @QueryParam("spans") Boolean spans,
+ @DefaultValue("true") @QueryParam("show-snippet") String snippetStr,
+ @DefaultValue("false") @QueryParam("show-tokens") String tokensStr,
+ @QueryParam("expand") String expansion,
+ // Highlights may also be a list of valid highlight classes
+ @QueryParam("hls") Boolean highlights) throws KustvaktException {
+
+ TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
+ try {
+ scopeService.verifyScope(tokenContext, OAuth2Scope.MATCH_INFO);
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+
+ Boolean expandToSentence = true;
+ if (expansion != null
+ && (expansion.equals("false") || expansion.equals("null"))) {
+ expandToSentence = false;
+ }
+ spans = spans != null ? spans : false;
+ Boolean snippet = true;
+ Boolean tokens = false;
+ if (snippetStr != null
+ && (snippetStr.equals("false") || snippetStr.equals("null")))
+ snippet = false;
+
+ if (tokensStr != null && (tokensStr.equals("true")
+ || tokensStr.equals("1") || tokensStr.equals("yes")))
+ tokens = true;
+
+ highlights = highlights != null ? highlights : false;
+ if (layers == null || layers.isEmpty())
+ layers = new HashSet<>();
+
+ try {
+ String results = searchService.retrieveMatchInfo(corpusId, docId,
+ textId, matchId, true, foundries,
+ tokenContext.getUsername(), headers, layers, spans, snippet,
+ tokens, expandToSentence, highlights);
+ return Response.ok(results).build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+
+ }
+
+ /*
+ * Returns the meta data fields of a certain document
+ */
+ // This is currently identical to LiteService#getMeta(),
+ // but may need auth code to work following policies
+ @GET
+ @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ @Path("{version}/corpus/{corpusId}/{docId}/{textId}")
+ public Response getMetadata (@PathParam("corpusId") String corpusId,
+ @PathParam("docId") String docId,
+ @PathParam("textId") String textId,
+ @QueryParam("fields") String fields,
+ @Context SecurityContext ctx,
+ @Context HttpHeaders headers
+ ) throws KustvaktException {
+ TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
+ try {
+ String results = searchService.retrieveDocMetadata(corpusId, docId,
+ textId, fields, tokenContext.getUsername(), headers);
+ return Response.ok(results).build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
+// EM: This web service requires Karang and is DISABLED.
+// @POST
+// @Path("{version}/colloc")
+// @Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public Response getCollocationBase (@QueryParam("q") String query) {
+ String result;
+ try {
+ result = searchService.getCollocationBase(query);
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ return Response.ok(result).build();
+ }
+
+ // @GET
+ // @Path("colloc")
+ // public Response getCollocationsAll(@Context SecurityContext
+ // ctx,
+ // @Context Locale locale, @QueryParam("props") String properties,
+ // @QueryParam("sfskip") Integer sfs,
+ // @QueryParam("sflimit") Integer limit, @QueryParam("q") String
+ // query,
+ // @QueryParam("ql") String ql, @QueryParam("context") Integer
+ // context,
+ // @QueryParam("foundry") String foundry,
+ // @QueryParam("paths") Boolean wPaths) {
+ // TokenContext tokenContext = (TokenContext)
+ // ctx.getUserPrincipal();
+ // ColloQuery.ColloQueryBuilder builder;
+ // KoralCollectionQueryBuilder cquery = new
+ // KoralCollectionQueryBuilder();
+ // String result;
+ // try {
+ // User user = controller.getUser(tokenContext.getUsername());
+ // Set<VirtualCollection> resources = ResourceFinder
+ // .search(user, VirtualCollection.class);
+ // for (KustvaktResource c : resources)
+ // cquery.addResource(((VirtualCollection) c).getQuery());
+ //
+ // builder = functions
+ // .buildCollocations(query, ql, properties, context, limit,
+ // sfs, foundry, new ArrayList<Dependency>(), wPaths,
+ // cquery);
+ //
+ // result = graphDBhandler
+ // .getResponse("distCollo", "q", builder.build().toJSON());
+ // }catch (KustvaktException e) {
+ // throw KustvaktResponseHandler.throwit(e);
+ // }catch (JsonProcessingException e) {
+ // throw
+ // KustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
+ // }
+ // return Response.ok(result).build();
+ // }
+
+ // /**
+ // * @param locale
+ // * @param properties a json object string containing field, op
+ // and value
+ // for the query
+ // * @param query
+ // * @param context
+ // * @return
+ // */
+ // @GET
+ // @Path("{type}/{id}/colloc")
+ // public Response getCollocations(@Context SecurityContext ctx,
+ // @Context Locale locale, @QueryParam("props") String properties,
+ // @QueryParam("sfskip") Integer sfs,
+ // @QueryParam("sflimit") Integer limit, @QueryParam("q") String
+ // query,
+ // @QueryParam("ql") String ql, @QueryParam("context") Integer
+ // context,
+ // @QueryParam("foundry") String foundry,
+ // @QueryParam("paths") Boolean wPaths, @PathParam("id") String
+ // id,
+ // @PathParam("type") String type) {
+ // ColloQuery.ColloQueryBuilder builder;
+ // type = StringUtils.normalize(type);
+ // id = StringUtils.decodeHTML(id);
+ // TokenContext tokenContext = (TokenContext)
+ // ctx.getUserPrincipal();
+ // String result;
+ // try {
+ // KoralCollectionQueryBuilder cquery = new
+ // KoralCollectionQueryBuilder();
+ // try {
+ // User user = controller.getUser(tokenContext.getUsername());
+ //
+ // KustvaktResource resource = this.resourceHandler
+ // .findbyStrId(id, user, type);
+ //
+ // if (resource instanceof VirtualCollection)
+ // cquery.addResource(
+ // ((VirtualCollection) resource).getQuery());
+ // else if (resource instanceof Corpus)
+ // cquery.addMetaFilter("corpusID",
+ // resource.getPersistentID());
+ // else
+ // throw KustvaktResponseHandler
+ // .throwit(StatusCodes.ILLEGAL_ARGUMENT,
+ // "Type parameter not supported", type);
+ //
+ // }catch (KustvaktException e) {
+ // throw KustvaktResponseHandler.throwit(e);
+ // }catch (NumberFormatException ex) {
+ // throw KustvaktResponseHandler
+ // .throwit(StatusCodes.ILLEGAL_ARGUMENT);
+ // }
+ //
+ // builder = functions
+ // .buildCollocations(query, ql, properties, context, limit,
+ // sfs, foundry, new ArrayList<Dependency>(), wPaths,
+ // cquery);
+ //
+ // result = graphDBhandler
+ // .getResponse("distCollo", "q", builder.build().toJSON());
+ //
+ // }catch (JsonProcessingException e) {
+ // throw
+ // KustvaktResponseHandler.throwit(StatusCodes.ILLEGAL_ARGUMENT);
+ // }catch (KustvaktException e) {
+ // throw KustvaktResponseHandler.throwit(e);
+ // }
+ //
+ // return Response.ok(result).build();
+ // }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
new file mode 100644
index 0000000..c053d45
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/core/web/controller/StatisticController.java
@@ -0,0 +1,111 @@
+package de.ids_mannheim.korap.core.web.controller;
+
+import java.util.List;
+import java.util.Locale;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+
+import de.ids_mannheim.korap.web.utils.ResourceFilters;
+import de.ids_mannheim.korap.core.service.StatisticService;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import de.ids_mannheim.korap.web.filter.PiwikFilter;
+
+/**
+ * Web services related to statistics
+ *
+ * @author hanl
+ * @author margaretha
+ *
+ * @date 08/11/2017
+ *
+ */
+@Controller
+@Path("{version}/statistics/")
+@ResourceFilters({ APIVersionFilter.class, PiwikFilter.class })
+@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
+public class StatisticController {
+
+ private static final boolean DEBUG = false;
+ private static Logger jlog =
+ LogManager.getLogger(StatisticController.class);
+ @Autowired
+ private CoreResponseHandler kustvaktResponseHandler;
+ @Autowired
+ private StatisticService service;
+
+ /**
+ * Returns statistics of the virtual corpus defined by the given
+ * corpusQuery parameter.
+ *
+ * @param context
+ * SecurityContext
+ * @param locale
+ * Locale
+ * @param cq
+ * a collection query specifying a virtual corpus
+ * @param corpusQuery
+ * (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") List<String> cq,
+ @QueryParam("corpusQuery") List<String> corpusQuery) {
+
+ String stats;
+ boolean isDeprecated = false;
+ try {
+ if (cq.isEmpty() && corpusQuery != null && !corpusQuery.isEmpty()) {
+ isDeprecated = true;
+ cq = corpusQuery;
+ }
+ stats = service.retrieveStatisticsForCorpusQuery(cq, isDeprecated);
+ if (DEBUG) {
+ jlog.debug("Stats: " + stats);
+ }
+
+ return Response
+ .ok(stats)
+ .header("X-Index-Revision", service.getIndexFingerprint())
+ .build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON + ";charset=utf-8")
+ public Response getStatisticsFromKoralQuery (
+ @Context SecurityContext context, @Context Locale locale,
+ String koralQuery) {
+ try {
+ String stats = service.retrieveStatisticsForKoralQuery(koralQuery);
+ return Response
+ .ok(stats)
+ .header("X-Index-Revision", service.getIndexFingerprint())
+ .build();
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java
new file mode 100644
index 0000000..fcf32d0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/AdminDao.java
@@ -0,0 +1,11 @@
+package de.ids_mannheim.korap.dao;
+
+import de.ids_mannheim.korap.user.User;
+
+public interface AdminDao {
+
+ void addAccount (User user);
+
+ boolean isAdmin (String userId);
+
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
new file mode 100644
index 0000000..43b7fd0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/AnnotationDao.java
@@ -0,0 +1,219 @@
+package de.ids_mannheim.korap.dao;
+
+import java.util.List;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.ids_mannheim.korap.constant.AnnotationType;
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationKey_;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer_;
+import de.ids_mannheim.korap.core.entity.Annotation_;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * AnnotationDao manages queries to database regarding annotations including
+ * foundry and layer pairs.
+ *
+ * @author margaretha
+ *
+ */
+@Repository
+public class AnnotationDao {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ /**
+ * Retrieves all foundry-layer pairs.
+ *
+ * @return a list of foundry-layer pairs.
+ */
+ @SuppressWarnings("unchecked")
+ public List<AnnotationLayer> getAllFoundryLayerPairs () {
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AnnotationLayer> query =
+ criteriaBuilder.createQuery(AnnotationLayer.class);
+ Root<AnnotationLayer> layer = query.from(AnnotationLayer.class);
+ layer.fetch(AnnotationLayer_.foundry);
+ layer.fetch(AnnotationLayer_.layer);
+ query.select(layer);
+ Query q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
+
+ /**
+ * Retrieves foundry-layer pairs and their values for the given
+ * foundry and layer. If layer is empty, retrieves data for all
+ * layer in the given foundry. If foundry is empty, retrieves data
+ * for all foundry and layer pairs.
+ *
+ * @param foundry
+ * a foundry code
+ * @param layer
+ * a layer code
+ * @return a list of foundry-layer pairs.
+ */
+ @SuppressWarnings("unchecked")
+ public List<AnnotationLayer> getAnnotationDescriptions (String foundry,
+ String layer) {
+
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<Object> query = criteriaBuilder.createQuery();
+ Root<AnnotationLayer> annotationPair =
+ query.from(AnnotationLayer.class);
+ annotationPair.fetch(AnnotationLayer_.foundry);
+ annotationPair.fetch(AnnotationLayer_.layer);
+ annotationPair.fetch(AnnotationLayer_.keys);
+
+ // EM: Hibernate bug in join n:m (see AnnotationPair.values).
+ // There should not be any redundant AnnotationPair.
+ // The redundancy can be also avoided with
+ // fetch=FetchType.EAGER
+ // because Hibernate does 2 selects.
+ query.distinct(true);
+ query = query.select(annotationPair);
+
+ if (!foundry.isEmpty()) {
+ Predicate foundryPredicate = criteriaBuilder.equal(annotationPair
+ .get(AnnotationLayer_.foundry).get(Annotation_.code),
+ foundry);
+ if (layer.isEmpty() || layer.equals("*")) {
+ query.where(foundryPredicate);
+ }
+ else {
+ Predicate layerPredicate = criteriaBuilder.equal(annotationPair
+ .get(AnnotationLayer_.layer).get(Annotation_.code),
+ layer);
+ Predicate andPredicate =
+ criteriaBuilder.and(foundryPredicate, layerPredicate);
+ query.where(andPredicate);
+ }
+ }
+
+ Query q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
+
+ public Annotation retrieveAnnotation (String code, String type) {
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<Annotation> query =
+ criteriaBuilder.createQuery(Annotation.class);
+
+ Root<Annotation> annotation = query.from(Annotation.class);
+ Predicate predicates = criteriaBuilder.and(
+ criteriaBuilder.equal(annotation.get(Annotation_.code), code),
+ criteriaBuilder.equal(annotation.get(Annotation_.type), type));
+ query.select(annotation).where(predicates);
+ Query q = entityManager.createQuery(query);
+ try {
+ return (Annotation) q.getSingleResult();
+ }
+ catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ public AnnotationLayer retrieveAnnotationLayer (String foundry,
+ String layer) {
+ Annotation ann1 = retrieveAnnotation(foundry, AnnotationType.FOUNDRY);
+ Annotation ann2 = retrieveAnnotation(layer, AnnotationType.LAYER);
+
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AnnotationLayer> query =
+ criteriaBuilder.createQuery(AnnotationLayer.class);
+
+ Root<AnnotationLayer> annotation = query.from(AnnotationLayer.class);
+ Predicate predicates = criteriaBuilder.and(
+ criteriaBuilder.equal(annotation.get(AnnotationLayer_.foundry),
+ ann1),
+ criteriaBuilder.equal(annotation.get(AnnotationLayer_.layer),
+ ann2));
+ query.select(annotation).where(predicates);
+ Query q = entityManager.createQuery(query);
+ try {
+ return (AnnotationLayer) q.getSingleResult();
+ }
+ catch (NoResultException e) {
+ return null;
+ }
+
+ }
+
+ @Transactional
+ public Annotation createAnnotation (String code, String type, String text,
+ String description) {
+ Annotation ann = new Annotation(code, type, text, description);
+ entityManager.persist(ann);
+ return ann;
+ }
+
+ @Transactional
+ public AnnotationLayer createAnnotationLayer (Annotation foundry,
+ Annotation layer) throws KustvaktException {
+ ParameterChecker.checkObjectValue(foundry, "foundry");
+ ParameterChecker.checkObjectValue(layer, "layer");
+
+ AnnotationLayer annotationLayer = new AnnotationLayer();
+ annotationLayer.setFoundryId(foundry.getId());
+ annotationLayer.setLayerId(layer.getId());
+ annotationLayer.setDescription(
+ foundry.getDescription() + " " + layer.getDescription());
+ entityManager.persist(annotationLayer);
+ return annotationLayer;
+ }
+
+ @Transactional
+ public void updateAnnotationLayer (AnnotationLayer layer) {
+ entityManager.merge(layer);
+ }
+
+ @Transactional
+ public void updateAnnotationKey (AnnotationKey key) {
+ entityManager.merge(key);
+ }
+
+ @Transactional
+ public AnnotationKey createAnnotationKey (AnnotationLayer layer,
+ Annotation key) {
+ AnnotationKey annotation =
+ new AnnotationKey(layer.getId(), key.getId());
+ entityManager.persist(annotation);
+ return annotation;
+ }
+
+ public AnnotationKey retrieveAnnotationKey (AnnotationLayer layer,
+ Annotation key) {
+
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<AnnotationKey> query =
+ criteriaBuilder.createQuery(AnnotationKey.class);
+
+ Root<AnnotationKey> annotation = query.from(AnnotationKey.class);
+ Predicate predicates = criteriaBuilder.and(
+ criteriaBuilder.equal(annotation.get(AnnotationKey_.layer),
+ layer),
+ criteriaBuilder.equal(annotation.get(AnnotationKey_.key), key));
+ query.select(annotation).where(predicates);
+ Query q = entityManager.createQuery(query);
+ try {
+ return (AnnotationKey) q.getSingleResult();
+ }
+ catch (NoResultException e) {
+ return null;
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
new file mode 100644
index 0000000..c26a39d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/ResourceDao.java
@@ -0,0 +1,82 @@
+package de.ids_mannheim.korap.dao;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.TypedQuery;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Root;
+
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.core.entity.Resource;
+import de.ids_mannheim.korap.core.entity.Resource_;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+/**
+ * ResourceDao manages SQL queries regarding resource info and layers.
+ *
+ * @author margaretha
+ *
+ */
+@Repository
+public class ResourceDao {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ /**
+ * Select all from the resource table
+ *
+ * @return a list of resources
+ */
+ public List<Resource> getAllResources () {
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<Resource> query =
+ criteriaBuilder.createQuery(Resource.class);
+ Root<Resource> resource = query.from(Resource.class);
+ query.select(resource);
+
+ TypedQuery<Resource> q = entityManager.createQuery(query);
+ return q.getResultList();
+ }
+
+ public Resource retrieveResource (String id) {
+ CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<Resource> query =
+ criteriaBuilder.createQuery(Resource.class);
+ Root<Resource> resource = query.from(Resource.class);
+ query.select(resource);
+ query.where(criteriaBuilder.equal(resource.get(Resource_.id), id));
+
+ Query q = entityManager.createQuery(query);
+ try {
+ return (Resource) q.getSingleResult();
+ }
+ catch (NoResultException e) {
+ return null;
+ }
+ }
+
+ @Transactional
+ public void createResource (String id, String germanTitle,
+ String englishTitle, String englishDescription,
+ Set<AnnotationLayer> layers) throws KustvaktException {
+ ParameterChecker.checkStringValue(id, "id");
+ ParameterChecker.checkStringValue(englishTitle, "en_title");
+ ParameterChecker.checkStringValue(germanTitle, "de_title");
+
+ Resource r = new Resource(id, germanTitle, englishTitle,
+ englishDescription, layers);
+ entityManager.persist(r);
+
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
new file mode 100644
index 0000000..c8f68b5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/FoundryDto.java
@@ -0,0 +1,59 @@
+package de.ids_mannheim.korap.dto;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data transfer object for annotation descriptions (e.g. for
+ * Kalamar).
+ *
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+@JsonInclude(Include.NON_EMPTY) // new fasterxml annotation, not used by current jersey version
+//@JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation, used by jersey
+public class FoundryDto {
+
+ private String code;
+ private String description;
+ private List<Layer> layers;
+
+ @Getter
+ @Setter
+ @JsonInclude(Include.NON_EMPTY)
+// @JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation used by jersey
+ public class Layer {
+ private String code;
+ private String description;
+ private Set<Key> keys;
+ }
+
+ @Getter
+ @Setter
+ @JsonInclude(Include.NON_EMPTY)
+// @JsonSerialize(include=Inclusion.NON_EMPTY) // old codehouse annotation used by jersey
+ public class Key implements Comparable<Key>{
+
+ private String code;
+ private String description;
+ private Map<String, String> values;
+
+ public Key (String code) {
+ this.code = code;
+ }
+
+ @Override
+ public int compareTo (Key k) {
+ return this.code.compareTo(k.code);
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java b/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
new file mode 100644
index 0000000..b9884d8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/LayerDto.java
@@ -0,0 +1,21 @@
+package de.ids_mannheim.korap.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Data transfer object for layer description (e.g. for KorapSRU).
+ *
+ * @author margaretha
+ *
+ */
+@Getter
+@Setter
+public class LayerDto {
+
+ private int id;
+ private String code;
+ private String layer;
+ private String foundry;
+ private String description;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java b/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
new file mode 100644
index 0000000..b192cfd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dto/converter/AnnotationConverter.java
@@ -0,0 +1,156 @@
+package de.ids_mannheim.korap.dto.converter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.core.entity.Annotation;
+import de.ids_mannheim.korap.core.entity.AnnotationKey;
+import de.ids_mannheim.korap.core.entity.AnnotationLayer;
+import de.ids_mannheim.korap.dto.FoundryDto;
+import de.ids_mannheim.korap.dto.FoundryDto.Key;
+import de.ids_mannheim.korap.dto.FoundryDto.Layer;
+import de.ids_mannheim.korap.dto.LayerDto;
+
+/**
+ * AnnotationConverter prepares data transfer objects (DTOs) from
+ * entities. The DTOs, for instance, are serialized into JSON in the
+ * controller classes and then sent as the response entity.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+public class AnnotationConverter {
+
+ /**
+ * Returns layer descriptions in a list of {@link LayerDto}s.
+ *
+ * @param pairs
+ * a list of {@link AnnotationLayer}s
+ * @return a list of {@link LayerDto}s
+ */
+ public List<LayerDto> convertToLayerDto (List<AnnotationLayer> pairs) {
+ List<LayerDto> layerDtos = new ArrayList<LayerDto>(pairs.size());
+ LayerDto dto;
+ String foundry, layer;
+ for (AnnotationLayer p : pairs) {
+ dto = new LayerDto();
+ dto.setId(p.getId());
+ dto.setDescription(p.getDescription());
+
+ foundry = p.getFoundry().getCode();
+ dto.setFoundry(foundry);
+
+ layer = p.getLayer().getCode();
+ dto.setLayer(layer);
+ dto.setCode(foundry + "/" + layer);
+ layerDtos.add(dto);
+ }
+
+ return layerDtos;
+ }
+
+ /**
+ * Returns foundry description in {@link FoundryDto}s
+ *
+ * @param pairs
+ * a list of {@link AnnotationLayer}s
+ * @param language
+ * @return a list of {@link FoundryDto}s
+ */
+ public List<FoundryDto> convertToFoundryDto (List<AnnotationLayer> pairs,
+ String language) {
+ List<FoundryDto> foundryDtos = new ArrayList<FoundryDto>(pairs.size());
+ Map<String, List<AnnotationLayer>> foundryMap = createFoundryMap(pairs);
+
+ for (String foundryCode : foundryMap.keySet()) {
+ List<AnnotationLayer> foundries = foundryMap.get(foundryCode);
+ List<Layer> layers = new ArrayList<Layer>(foundries.size());
+ FoundryDto dto = null;
+
+ for (AnnotationLayer f : foundries) {
+ if (dto == null) {
+ Annotation foundry = f.getFoundry();
+ dto = new FoundryDto();
+ if (language.equals("de")) {
+ dto.setDescription(foundry.getGermanDescription());
+ }
+ else {
+ dto.setDescription(foundry.getDescription());
+ }
+ dto.setCode(foundry.getCode());
+ }
+
+ Annotation layer = f.getLayer();
+ Set<Key> keys = new TreeSet<>();
+
+ for (AnnotationKey ak : f.getKeys()) {
+ Annotation a = ak.getKey();
+ Map<String, String> values = new TreeMap<>();
+
+ Key key = dto.new Key(a.getCode());
+ if (language.equals("de")) {
+ key.setDescription(a.getGermanDescription());
+ for (Annotation v : ak.getValues()) {
+ values.put(v.getCode(), v.getGermanDescription());
+ }
+
+ }
+ else {
+ key.setDescription(a.getDescription());
+ for (Annotation v : ak.getValues()) {
+ values.put(v.getCode(), v.getDescription());
+ }
+ }
+ key.setValues(values);
+ keys.add(key);
+ }
+
+ Layer l = dto.new Layer();
+ l.setCode(layer.getCode());
+
+ if (language.equals("de")) {
+ l.setDescription(layer.getGermanDescription());
+ }
+ else {
+ l.setDescription(layer.getDescription());
+ }
+
+ l.setKeys(keys);
+ layers.add(l);
+ }
+
+ dto.setLayers(layers);
+ foundryDtos.add(dto);
+ }
+
+ return foundryDtos;
+ }
+
+ private Map<String, List<AnnotationLayer>> createFoundryMap (
+ List<AnnotationLayer> pairs) {
+ Map<String, List<AnnotationLayer>> foundries =
+ new HashMap<String, List<AnnotationLayer>>();
+ for (AnnotationLayer p : pairs) {
+ String foundryCode = p.getFoundry().getCode();
+ if (foundries.containsKey(foundryCode)) {
+ foundries.get(foundryCode).add(p);
+ }
+ else {
+ List<AnnotationLayer> foundryList =
+ new ArrayList<AnnotationLayer>();
+ foundryList.add(p);
+ foundries.put(foundryCode, foundryList);
+ }
+ }
+
+ return foundries;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java b/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java
new file mode 100644
index 0000000..bf8cca1
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/encryption/RandomCodeGenerator.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.encryption;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.stream.Collectors;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/**
+ * Generates a random string that can be used for tokens, client id,
+ * client secret, etc.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+public class RandomCodeGenerator {
+
+ public static String alphanumeric = "34679bdfhmnprtFGHJLMNPRT";
+ public static List<Character> charList = alphanumeric.chars()
+ .mapToObj(c -> (char) c).collect(Collectors.toList());
+
+ public final static List<String> LIMITED_CHARACTERS =
+ Arrays.asList(new String[] { "3", "4", "6", "7", "9", "b", "d", "f",
+ "h", "m", "n", "p", "r", "t", "F", "G", "H", "J", "L", "M",
+ "N", "P", "R", "T" });
+
+ private Logger log = LogManager.getLogger(RandomCodeGenerator.class);
+
+ @Autowired
+ public KustvaktConfiguration config;
+
+ public static SecureRandom secureRandom;
+
+ @PostConstruct
+ public void init () throws NoSuchAlgorithmException {
+ String algorithm = config.getSecureRandomAlgorithm();
+ if (!algorithm.isEmpty()) {
+ secureRandom = SecureRandom.getInstance(algorithm);
+ }
+ else {
+ secureRandom = new SecureRandom();
+ }
+ log.info(
+ "Secure random algorithm: " + secureRandom.getAlgorithm());
+ }
+
+ public String createRandomCode (KustvaktConfiguration c)
+ throws KustvaktException, NoSuchAlgorithmException {
+ config = c;
+ init();
+ return createRandomCode();
+ }
+
+ public String createRandomCode () throws KustvaktException {
+ UUID randomUUID = UUID.randomUUID();
+ byte[] uuidBytes = randomUUID.toString().getBytes();
+ byte[] secureBytes = new byte[3];
+ secureRandom.nextBytes(secureBytes);
+
+ byte[] bytes = ArrayUtils.addAll(uuidBytes, secureBytes);
+
+ try {
+ MessageDigest md = MessageDigest
+ .getInstance(config.getMessageDigestAlgorithm());
+ md.update(bytes);
+ byte[] digest = md.digest();
+ String code = Base64.encodeBase64URLSafeString(digest);
+ md.reset();
+ return code;
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new KustvaktException(StatusCodes.INVALID_ALGORITHM,
+ config.getMessageDigestAlgorithm()
+ + "is not a valid MessageDigest algorithm");
+ }
+ }
+
+ public String filterRandomCode (String code) {
+ StringBuffer s = new StringBuffer();
+ for (char c : code.toCharArray()) {
+ if (!charList.contains(c)) {
+ int n = ThreadLocalRandom.current().nextInt(0,
+ charList.size());
+ c = charList.get(n);
+ }
+ s.append(c);
+ }
+ return s.toString();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java
new file mode 100644
index 0000000..26190cc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/DatabaseException.java
@@ -0,0 +1,50 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+
+import java.util.Arrays;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+public class DatabaseException extends KustvaktException {
+
+ private DatabaseException (Object userid, Integer status, String message,
+ String args, Exception e) {
+ super(String.valueOf(userid), status, message, args, e);
+ }
+
+ public DatabaseException (Object userid, String target, Integer status, String message,
+ String ... args) {
+ this(null, userid, target, status, message);
+ }
+
+ public DatabaseException (Exception e, Object userid, String target, Integer status, String message,
+ String ... args) {
+ this(userid, status, message, Arrays.asList(args).toString(), e);
+ AuditRecord record = new AuditRecord(AuditRecord.CATEGORY.DATABASE);
+ record.setUserid(String.valueOf(userid));
+ record.setStatus(status);
+ record.setTarget(target);
+ record.setArgs(this.getEntity());
+ this.records.add(record);
+ }
+
+
+ public DatabaseException (KustvaktException e, Integer status, String ... args) {
+ this(e.getUserid(), e.getStatusCode(), e.getMessage(), e.getEntity(), e);
+ AuditRecord record = AuditRecord.dbRecord(e.getUserid(), status, args);
+ record.setField_1(e.string());
+ this.records.addAll(e.getRecords());
+ this.records.add(record);
+ }
+
+
+ @Override
+ public String string () {
+ return "DBExcpt{" + "status=" + getStatusCode() + ", message="
+ + getMessage() + ", args=" + getEntity() + ", userid="
+ + this.getUserid() + '}';
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java
new file mode 100644
index 0000000..d52238b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/EmptyResultException.java
@@ -0,0 +1,20 @@
+package de.ids_mannheim.korap.exceptions;
+
+/**
+ * @author hanl
+ * @date 25/03/2014
+ */
+@Deprecated
+// even useful anymore?
+public class EmptyResultException extends KustvaktException {
+
+ public EmptyResultException (String message, String entity) {
+ super(StatusCodes.NO_RESULT_FOUND, message, entity);
+ }
+
+
+ public EmptyResultException (String entity) {
+ super(StatusCodes.NO_RESULT_FOUND, "No entity found for id", entity);
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
new file mode 100644
index 0000000..6ccbbce
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/KustvaktException.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.exceptions;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+//import de.ids_mannheim.korap.constant.TokenType;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author hanl
+ * @date 11/12/2013
+ */
+@Setter
+@Getter
+public class KustvaktException extends Exception {
+
+ private static final long serialVersionUID = -1955783565699446322L;
+ protected List<AuditRecord> records = new ArrayList<>();
+ private String userid;
+ private Integer statusCode;
+ private int responseStatus;
+
+ private String entity;
+ private String notification;
+ private boolean isNotification;
+// private TokenType authType;
+ private URI redirectUri;
+
+ public KustvaktException (int status) {
+ this.statusCode = status;
+ }
+
+ public KustvaktException (int status, String ... args) {
+ super(args[0]);
+ this.statusCode = status;
+ String[] subarray = Arrays.copyOfRange(args, 1, args.length);
+ this.entity = Arrays.asList(subarray).toString();
+ }
+
+ public KustvaktException (int status, String notification, boolean isNotification) {
+ this.statusCode = status;
+ this.notification = notification;
+ this.isNotification = isNotification;
+ }
+
+ public boolean hasNotification () {
+ return isNotification;
+ }
+
+ public KustvaktException (Object userid, int status) {
+ this(status);
+ this.userid = String.valueOf(userid);
+ }
+
+ // add throwable to parameters
+ public KustvaktException (Object userid, int status, String message,
+ String entity) {
+ this(userid, status, message, entity, null);
+ }
+
+
+ public KustvaktException (Object userid, int status, String message,
+ String entity, Exception e) {
+ super(message, e);
+ this.statusCode = status;
+ this.entity = entity;
+ this.userid = String.valueOf(userid);
+ }
+
+
+ public KustvaktException (Object userid, int status, String entity) {
+ super(StatusCodes.getMessage(status));
+ this.statusCode = status;
+ this.entity = entity;
+ this.userid = String.valueOf(userid);
+ }
+
+ public KustvaktException (int status, String message, String entity) {
+ super(message);
+ this.statusCode = status;
+ this.entity = entity;
+ }
+
+ public KustvaktException (int status, String message, String entity, Throwable e) {
+ super(message, e);
+ this.statusCode = status;
+ this.entity = entity;
+ }
+
+ public KustvaktException (int status, String message, Throwable e) {
+ super(message, e);
+ this.statusCode = status;
+ }
+
+
+ public KustvaktException (Throwable cause, int status) {
+ super(cause);
+ this.statusCode = status;
+ }
+
+
+ public KustvaktException (String message, Throwable cause, int status) {
+ super(message, cause);
+ this.statusCode = status;
+ }
+
+
+
+ public KustvaktException (String notification) {
+ this.notification = notification;
+ isNotification = true;
+ }
+
+ public String string () {
+ return "Excpt{" + "status=" + getStatusCode() + ", message="
+ + getMessage() + ", args=" + getEntity() + ", userid=" + userid
+ + '}';
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java
new file mode 100644
index 0000000..99ddebe
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/ServiceException.java
@@ -0,0 +1,62 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+// should be a http exception that responds to a service point
+// is the extension of the notauthorized exception!
+@Setter(AccessLevel.PROTECTED)
+@Getter(AccessLevel.PROTECTED)
+public class ServiceException extends Exception {
+
+ protected List<AuditRecord> records = new ArrayList<>();
+ private static final Logger jlog = LogManager
+ .getLogger(ServiceException.class);
+
+ private int status;
+ private String entity;
+ private Object userid;
+
+
+ protected ServiceException (Object userid, Integer status, String message,
+ String args) {
+ super(message);
+ this.userid = userid;
+ this.status = status;
+ this.entity = args;
+ AuditRecord record = AuditRecord.serviceRecord(userid, status, args);
+ this.records.add(record);
+ }
+
+
+ @Deprecated
+ public ServiceException (Object userid, Integer status, String ... args) {
+ this(userid, status, StatusCodes.getMessage(status), Arrays
+ .asList(args).toString());
+ }
+
+
+ public ServiceException (Integer status, KustvaktException ex) {
+ this(ex.getUserid(), ex.getStatusCode(), ex.getMessage(), ex
+ .getEntity());
+ AuditRecord record = AuditRecord.serviceRecord(ex.getUserid(), status,
+ ex.getEntity());
+ record.setField_1(ex.toString());
+ this.records.add(record);
+ jlog.error("Exception: " + ex.toString());
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
new file mode 100644
index 0000000..c1777a4
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/StatusCodes.java
@@ -0,0 +1,220 @@
+package de.ids_mannheim.korap.exceptions;
+
+import java.util.Properties;
+
+import de.ids_mannheim.korap.config.ConfigLoader;
+
+/**
+ * @author hanl, margaretha
+ * @date 07/09/2014
+ */
+public class StatusCodes {
+
+ /**
+ * 100 status codes for standard system errors
+ */
+ public static final int GENERAL_ERROR = 100;
+ public static final int NO_RESULT_FOUND = 101;
+ public static final int UNSUPPORTED_AUTHENTICATION_SCHEME = 102;
+ public static final int UNSUPPORTED_OPERATION = 103;
+ public static final int ILLEGAL_ARGUMENT = 104;
+ public static final int MISSING_PARAMETER = 105;
+ public static final int CONNECTION_ERROR = 106;
+ public static final int INVALID_ARGUMENT = 107;
+ public static final int NOT_SUPPORTED = 108;
+ public static final int NOT_ALLOWED = 109;
+ public static final int HTTPS_REQUIRED = 110;
+ public static final int INVALID_ALGORITHM = 111;
+ public static final int UNSUPPORTED_API_VERSION = 112;
+ public static final int NON_UNIQUE_RESULT_FOUND = 113;
+ public static final int NO_RESOURCE_FOUND = 114;
+ public static final int DEPRECATED = 115;
+ public static final int CACHING_VC = 116;
+ public static final int NETWORK_ENDPOINT_NOT_AVAILABLE = 117;
+ public static final int SEARCH_NETWORK_ENDPOINT_FAILED = 118;
+
+ /**
+ * 200 status codes general JSON serialization error
+ */
+
+ public static final int SERIALIZATION_FAILED = 200;
+ public static final int DESERIALIZATION_FAILED = 201;
+ public static final int MISSING_ATTRIBUTE = 202;
+ public static final int INVALID_ATTRIBUTE = 203;
+ public static final int UNSUPPORTED_VALUE = 204;
+
+ /**
+ * 300 status codes for query language and serialization
+ * see Koral (de.ids_mannheim.korap.query.serialize.util.StatusCodes)
+ */
+
+ /**
+ * 400 status codes for rewrite functions
+ */
+
+ public static final int REWRITE_ERROR_DEFAULT = 400;
+ public static final int NON_PUBLIC_FIELD_IGNORED = 401;
+ public static final int PIPE_FAILED = 402;
+
+// public static final int UNSUPPORTED_RESOURCE = 402;
+ // public static final int REWRITE_FAILED = 403;
+ //public static final int UNSUPPORTED_FOUNDRY = 403;
+ //public static final int UNSUPPORTED_CORPUS = 404;
+ //public static final int UNSUPPORTED_LAYER = 405;
+ // make a distinction between no and invalid vc?
+ //public static final int UNSUPPORTED_COLLECTION = 406;
+ //public static final int CORPUS_REWRITE = 407;
+ //public static final int FOUNDRY_REWRITE = 408;
+ //public static final int FOUNDRY_INJECTION = 409;
+ // public static final int MISSING_RESOURCE = 405;
+// public static final int NO_POLICY_TARGET = 406;
+// public static final int NO_POLICY_CONDITION = 407;
+// public static final int NO_POLICY_PERMISSION = 408;
+// public static final int NO_POLICIES = 409;
+
+
+
+ /**
+ * 500 status codes for access control related components (also
+ * policy rewrite)
+ */
+ // todo: extend according to policy rewrite possible!
+ // policy errors
+
+
+ // database codes
+ public static final int DB_GET_FAILED = 500;
+ public static final int DB_INSERT_FAILED = 501;
+ public static final int DB_DELETE_FAILED = 502;
+ public static final int DB_UPDATE_FAILED = 503;
+
+ public static final int DB_GET_SUCCESSFUL = 504;
+ public static final int DB_INSERT_SUCCESSFUL = 505;
+ public static final int DB_DELETE_SUCCESSFUL = 506;
+ public static final int DB_UPDATE_SUCCESSFUL = 507;
+ public static final int DB_ENTRY_EXISTS = 508;
+
+
+ // public static final int ARGUMENT_VALIDATION_FAILURE = 700;
+ // public static final int ARGUMENT_VALIDATION_FAILURE = 701;
+
+ // service status codes
+ public static final int CREATE_ACCOUNT_SUCCESSFUL = 700;
+ public static final int CREATE_ACCOUNT_FAILED = 701;
+ public static final int DELETE_ACCOUNT_SUCCESSFUL = 702;
+ public static final int DELETE_ACCOUNT_FAILED = 703;
+ public static final int UPDATE_ACCOUNT_SUCCESSFUL = 704;
+ public static final int UPDATE_ACCOUNT_FAILED = 705;
+
+ public static final int GET_ACCOUNT_SUCCESSFUL = 706;
+ public static final int GET_ACCOUNT_FAILED = 707;
+
+
+ public static final int STATUS_OK = 1000;
+ public static final int NOTHING_CHANGED = 1001;
+ public static final int REQUEST_INVALID = 1002;
+
+// public static final int ACCESS_DENIED = 1003;
+
+
+ // User group and member
+ public static final int GROUP_MEMBER_EXISTS = 1601;
+ public static final int GROUP_MEMBER_INACTIVE = 1602;
+ public static final int GROUP_MEMBER_DELETED = 1603;
+ public static final int GROUP_MEMBER_NOT_FOUND = 1604;
+ public static final int INVITATION_EXPIRED = 1605;
+ public static final int GROUP_DELETED = 1606;
+
+ /**
+ * 1800 Oauth2 and OpenID
+ */
+
+ public static final int OAUTH2_SYSTEM_ERROR = 1800;
+
+ public static final int CLIENT_REGISTRATION_FAILED = 1801;
+ public static final int CLIENT_DEREGISTRATION_FAILED = 1802;
+ public static final int CLIENT_AUTHENTICATION_FAILED = 1803;
+ public static final int CLIENT_AUTHORIZATION_FAILED = 1804;
+ public static final int CLIENT_NOT_FOUND = 1805;
+ public static final int INVALID_REDIRECT_URI = 1806;
+ public static final int MISSING_REDIRECT_URI = 1807;
+ public static final int INVALID_SCOPE = 1808;
+ public static final int INVALID_AUTHORIZATION = 1809;
+ public static final int INVALID_REFRESH_TOKEN = 1810;
+
+ public static final int UNSUPPORTED_GRANT_TYPE = 1811;
+ public static final int UNSUPPORTED_AUTHENTICATION_METHOD = 1812;
+
+ public static final int ID_TOKEN_CLAIM_ERROR = 1813;
+ public static final int ID_TOKEN_SIGNING_FAILED = 1814;
+ public static final int USER_REAUTHENTICATION_REQUIRED = 1815;
+
+ public static final int INVALID_REFRESH_TOKEN_EXPIRY = 1816;
+
+ /**
+ * 1850 Plugins
+ */
+
+ public static final int PLUGIN_NOT_PERMITTED = 1850;
+ public static final int PLUGIN_HAS_BEEN_INSTALLED = 1851;
+
+
+ /**
+ * 1900 User account and logins
+ */
+
+ public static final int LOGIN_SUCCESSFUL = 1900;
+ public static final int ALREADY_LOGGED_IN = 1901;
+
+ public static final int LOGOUT_SUCCESSFUL = 1902;
+ public static final int LOGOUT_FAILED = 1903;
+
+ public static final int ACCOUNT_CONFIRMATION_FAILED = 1904;
+ public static final int PASSWORD_RESET_FAILED = 1905;
+
+ /**
+ * 2000 status and error codes concerning authentication
+ *
+ * Response with WWW-Authenticate header will be created
+ * for all KustvaktExceptions with status codes 2001 or greater
+ *
+ * MH: service level messages and callbacks
+ */
+
+ @Deprecated
+ public static final int INCORRECT_ADMIN_TOKEN = 2000;
+
+ public static final int AUTHENTICATION_FAILED = 2001;
+ public static final int LOGIN_FAILED = 2002;
+ public static final int EXPIRED = 2003;
+ public static final int BAD_CREDENTIALS = 2004;
+ public static final int ACCOUNT_NOT_CONFIRMED = 2005;
+ public static final int ACCOUNT_DEACTIVATED = 2006;
+
+ // public static final int CLIENT_AUTHORIZATION_FAILED = 2013;
+ public static final int AUTHORIZATION_FAILED = 2010;
+ public static final int INVALID_ACCESS_TOKEN = 2011;
+
+ // 2020 - 2029 reserviert für LDAP-Fehlercodes - 21.04.17/FB
+ public static final int LDAP_BASE_ERRCODE = 2020;
+
+ /**/
+ private static StatusCodes codes;
+
+ private final Properties props;
+
+ private StatusCodes () {
+ this.props = ConfigLoader.loadProperties("codes.info");
+ }
+
+
+ public static final String getMessage (int code) {
+ return getCodes().props.getProperty(String.valueOf(code));
+ }
+
+ public static StatusCodes getCodes () {
+ if (codes == null) codes = new StatusCodes();
+ return codes;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java b/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java
new file mode 100644
index 0000000..534419a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/exceptions/WrappedException.java
@@ -0,0 +1,38 @@
+package de.ids_mannheim.korap.exceptions;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+
+import java.util.Arrays;
+
+/**
+ * @author hanl
+ * @date 08/04/2015
+ */
+// should be a http exception that responds to a service point
+// is the extension of the notauthorized exception!
+public class WrappedException extends KustvaktException {
+
+ private WrappedException (Object userid, Integer status, String message,
+ String args, Exception rootCause) {
+ super(String.valueOf(userid), status, message, args, rootCause);
+ }
+
+
+ public WrappedException (Object userid, Integer status, String ... args) {
+ this(userid, status, "", Arrays.asList(args).toString(), null);
+ AuditRecord record = AuditRecord.serviceRecord(userid, status, args);
+ this.records.add(record);
+ }
+
+
+ public WrappedException (KustvaktException e, Integer status,
+ String ... args) {
+ this(e.getUserid(), e.getStatusCode(), e.getMessage(), e.getEntity(), e);
+ AuditRecord record = AuditRecord.serviceRecord(e.getUserid(), status,
+ args);
+ record.setField_1(e.string());
+ this.records.addAll(e.getRecords());
+ this.records.add(record);
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java
new file mode 100644
index 0000000..46f519a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/EncryptionIface.java
@@ -0,0 +1,75 @@
+package de.ids_mannheim.korap.interfaces;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+
+public interface EncryptionIface {
+
+ public enum Encryption {
+ @Deprecated
+ SIMPLE, ESAPICYPHER, BCRYPT
+ }
+
+
+ /**
+ * One-way hashing of String input. Used to canonicalize
+ *
+ * @param input
+ * @param salt
+ * @return
+ */
+ public String secureHash (String input, String salt)
+ throws KustvaktException;
+
+
+ public String secureHash (String input);
+
+
+ /**
+ * @param plain
+ * @param hash
+ * @param salt
+ * @return
+ */
+ public boolean checkHash (String plain, String hash, String salt);
+
+
+ public boolean checkHash (String plain, String hash);
+
+
+ /**
+ * create random String to be used as authentication token
+ *
+ * @return
+ */
+ public String createToken (boolean hash, Object ... obj);
+
+
+ public String createToken ();
+
+
+ /**
+ * create a random Integer to be used as ID for databases
+ *
+ * @return
+ */
+ public String createRandomNumber (Object ... obj);
+
+
+ public String encodeBase ();
+
+
+ // @Deprecated
+ //public Map<String, Object> validateMap (Map<String, Object> map)
+ // throws KustvaktException;
+
+
+ //@Deprecated
+ //public String validateEntry (String input, String type)
+ // throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
new file mode 100644
index 0000000..9bcf614
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/EntityHandlerIface.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.interfaces;
+
+import de.ids_mannheim.korap.exceptions.EmptyResultException;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * User: hanl
+ * Date: 8/19/13
+ * Time: 11:04 AM
+ */
+public interface EntityHandlerIface {
+
+ User getAccount (String username) throws EmptyResultException,
+ KustvaktException;
+
+
+ int updateAccount (User user) throws KustvaktException;
+
+
+ int createAccount (User user) throws KustvaktException;
+
+
+ int deleteAccount (Integer userid) throws KustvaktException;
+
+
+ int truncate () throws KustvaktException;
+
+
+ int resetPassphrase (String username, String uriToken, String passphrase)
+ throws KustvaktException;
+
+
+ int activateAccount (String username, String uriToken)
+ throws KustvaktException;
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java
new file mode 100644
index 0000000..42a2a74
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktBaseDaoInterface.java
@@ -0,0 +1,14 @@
+package de.ids_mannheim.korap.interfaces;
+
+/**
+ * @author hanl
+ * @date 11/02/2016
+ */
+public interface KustvaktBaseDaoInterface {
+
+ int size ();
+
+
+ int truncate ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java
new file mode 100644
index 0000000..2f66b95
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/KustvaktTypeInterface.java
@@ -0,0 +1,10 @@
+package de.ids_mannheim.korap.interfaces;
+
+/**
+ * @author hanl
+ * @date 11/02/2016
+ */
+public interface KustvaktTypeInterface<T> {
+
+ Class<T> type ();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
new file mode 100644
index 0000000..5e6866b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/AuditingIface.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.user.User;
+import edu.emory.mathcs.backport.java.util.Collections;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * User: hanl
+ * Date: 8/20/13
+ * Time: 10:45 AM
+ */
+//fixme: move table to different database!
+public abstract class AuditingIface {
+
+ protected static int BATCH_SIZE = 15;
+ private final List<AuditRecord> records = Collections
+ .synchronizedList(new ArrayList<>(BATCH_SIZE + 5));
+ private final List<AuditRecord> buffer = new ArrayList<>(BATCH_SIZE + 5);
+
+
+ public abstract <T extends AuditRecord> List<T> retrieveRecords (
+ AuditRecord.CATEGORY category, DateTime day, DateTime until,
+ boolean exact, int limit);
+
+
+ public abstract <T extends AuditRecord> List<T> retrieveRecords (
+ AuditRecord.CATEGORY category, User user, int limit);
+
+
+ public abstract <T extends AuditRecord> List<T> retrieveRecords (
+ LocalDate day, int hitMax);
+
+
+ public abstract <T extends AuditRecord> List<T> retrieveRecords (
+ String userID, LocalDate start, LocalDate end, int hitMax);
+
+
+ private void addAndRun (AuditRecord record) {
+ if (buffer.size() > BATCH_SIZE) {
+ records.clear();
+ records.addAll(buffer);
+ new Thread(new Runnable() {
+ @Override
+ public void run () {
+ apply();
+ }
+ }).start();
+ buffer.clear();
+ }
+ if (buffer.size() <= BATCH_SIZE)
+ buffer.add(record);
+ }
+
+
+ public <T extends AuditRecord> void audit (T request) {
+ addAndRun(request);
+ }
+
+
+ public <T extends AuditRecord> void audit (List<T> requests) {
+ for (T rec : requests)
+ addAndRun(rec);
+ }
+
+
+ public abstract void apply ();
+
+
+ protected List<AuditRecord> getRecordsToSave () {
+ return this.records;
+ }
+
+
+ public abstract void finish ();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
new file mode 100644
index 0000000..9fd0f79
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/PersistenceClient.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+import lombok.Getter;
+
+import java.io.*;
+
+@Getter
+public abstract class PersistenceClient<SOURCE> {
+
+ private SOURCE source;
+ private TYPE type;
+
+ protected String database;
+ private InputStream schema;
+
+
+ public PersistenceClient (String database, TYPE type) {
+ this.type = type;
+ this.database = database;
+ }
+
+
+ public PersistenceClient (TYPE type) {
+ this.type = type;
+ }
+
+
+ public PersistenceClient () {}
+
+
+ protected void setSource (SOURCE conn) {
+ this.source = conn;
+ }
+
+
+ public void setDatabase (String name) {
+ this.database = name;
+ }
+
+
+ public void setSchema (String schema_path) throws FileNotFoundException {
+ this.schema = new FileInputStream(new File(schema_path));
+ }
+
+
+ // for spring configuration
+ @Deprecated
+ public void setSchema (InputStream schema) throws IOException {
+ this.schema = schema;
+ }
+
+
+ public abstract boolean checkDatabase ();
+
+
+ public abstract void createDatabase () throws IOException;
+
+ public enum TYPE {
+ SQL, CASSANDRA
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java
new file mode 100644
index 0000000..23dfbc4
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/db/UserDataDbIface.java
@@ -0,0 +1,32 @@
+package de.ids_mannheim.korap.interfaces.db;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.interfaces.KustvaktTypeInterface;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.Userdata;
+
+/**
+ * @author hanl
+ * @date 27/01/2016
+ */
+public interface UserDataDbIface<T extends Userdata> extends
+ KustvaktTypeInterface<T> {
+
+ public int store (T data) throws KustvaktException;
+
+
+ public int update (T data) throws KustvaktException;
+
+
+ public T get (Integer id) throws KustvaktException;
+
+
+ public T get (User user) throws KustvaktException;
+
+
+ public int delete (T data) throws KustvaktException;
+
+
+ public int deleteAll () throws KustvaktException;
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java b/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
new file mode 100644
index 0000000..86e533e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/interfaces/defaults/DefaultAuditing.java
@@ -0,0 +1,91 @@
+package de.ids_mannheim.korap.interfaces.defaults;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.config.ContextHolder;
+import de.ids_mannheim.korap.config.Configurable;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.user.User;
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author hanl
+ * @date 05/06/2015
+ */
+@Configurable(ContextHolder.KUSTVAKT_AUDITING)
+public class DefaultAuditing extends AuditingIface {
+
+ private FileOutputStream stream;
+
+
+ public DefaultAuditing () {
+ try {
+ File f = new File("logs");
+ f.mkdirs();
+ stream = new FileOutputStream(new File(f, "default_audit.log"));
+ }
+ catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @Override
+ public <T extends AuditRecord> List<T> retrieveRecords (
+ AuditRecord.CATEGORY category, DateTime day, DateTime until,
+ boolean exact, int limit) {
+ throw new UnsupportedOperationException("Operation not supported!");
+ }
+
+
+ @Override
+ public <T extends AuditRecord> List<T> retrieveRecords (
+ AuditRecord.CATEGORY category, User user, int limit) {
+ throw new UnsupportedOperationException("Operation not supported!");
+ }
+
+
+ @Override
+ public <T extends AuditRecord> List<T> retrieveRecords (LocalDate day,
+ int hitMax) {
+ throw new UnsupportedOperationException("Operation not supported!");
+ }
+
+
+ @Override
+ public <T extends AuditRecord> List<T> retrieveRecords (String userID,
+ LocalDate start, LocalDate end, int hitMax) {
+ throw new UnsupportedOperationException("Operation not supported!");
+ }
+
+
+ @Override
+ public void apply () {
+ List<AuditRecord> rcs = getRecordsToSave();
+ try {
+ for (AuditRecord r : rcs)
+ stream.write((r.toString() + "\n").getBytes());
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ @Override
+ public void finish () {
+ try {
+ stream.flush();
+ stream.close();
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
new file mode 100644
index 0000000..53bfaf0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/oauth2/service/OAuth2ScopeService.java
@@ -0,0 +1,24 @@
+package de.ids_mannheim.korap.oauth2.service;
+
+import de.ids_mannheim.korap.constant.OAuth2Scope;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/**
+ * @author margaretha
+ *
+ */
+public interface OAuth2ScopeService {
+
+ /**
+ * Verifies whether the given token context contains the required
+ * scope
+ *
+ * @param context a token context containing authorized scopes
+ * @param requiredScope the required scope
+ * @throws KustvaktException
+ */
+ void verifyScope (TokenContext context, OAuth2Scope requiredScope)
+ throws KustvaktException;
+
+}
\ No newline at end of file
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
new file mode 100644
index 0000000..530eb86
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
@@ -0,0 +1,73 @@
+package de.ids_mannheim.korap.rewrite;
+
+import java.util.Iterator;
+
+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.user.User;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/** EM: not used anymore. This rewrite was to remove an empty koral:doc group in operands.
+ *
+ * @author hanl
+ * @date 28/07/2015
+ */
+public class CollectionCleanRewrite implements RewriteTask.RewriteNodeAt {
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) {
+ JsonNode jsonNode = process(node.rawNode());
+ return node.wrapNode(jsonNode);
+ }
+
+
+ private JsonNode process (JsonNode root) {
+ JsonNode sub = root;
+ if (root.isObject()) {
+ if (root.has("operands")) {
+ JsonNode node = root.at("/operands");
+ Iterator<JsonNode> it = node.elements();
+ while (it.hasNext()) {
+ JsonNode n = it.next();
+ JsonNode s = process(n);
+ if (s == null)
+ it.remove();
+ }
+
+ int count = node.size();
+ // remove group element and replace with single doc
+ if (count == 1)
+ sub = node.path(0);
+ // indicate empty group
+ else if (count == 0) // can't do anything here -- fixme: edge case?!
+ return null;
+ }
+
+ // what happens to array nodes?
+ if (!root.equals(sub)) {
+ if (sub.isObject()) {
+ ObjectNode ob = (ObjectNode) root;
+ ob.remove(Arrays.asList(new String[] { "@type",
+ "operation", "operands" }));
+ ob.putAll((ObjectNode) sub);
+ }
+ }
+ }
+ return root;
+ }
+
+
+ @Override
+ public JsonNode rewriteResult (KoralNode node) {
+ return null;
+ }
+
+
+ @Override
+ public String at () {
+ return "/collection";
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
new file mode 100644
index 0000000..9146e5f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
@@ -0,0 +1,62 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.rewrite.KoralNode.RewriteIdentifier;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+
+/**
+ * @author hanl, margaretha
+ * @date 30/06/2015
+ */
+public class FoundryInject implements RewriteTask.IterableRewritePath {
+
+ @Autowired
+ protected LayerMapper mapper;
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) throws KustvaktException {
+
+ if (node.get("@type").equals("koral:span")) {
+ if (!node.isMissingNode("/wrap")){
+ node = node.at("/wrap");
+ JsonNode term = rewriteQuery(node, config, user).rawNode();
+ node.replaceAt("/wrap", term, new RewriteIdentifier("koral:term", "replace"));
+ }
+ }
+ else if (node.get("@type").equals("koral:term") && !node.has("foundry")) {
+ String layer;
+ if (node.has("layer")){
+ layer = node.get("layer");
+ }
+ else{
+ layer = node.get("key");
+ }
+ UserSettingProcessor settingProcessor = null;
+ if (user!=null){
+ settingProcessor = user.getUserSettingProcessor();
+ }
+ String foundry = mapper.findFoundry(layer, settingProcessor);
+ if (foundry != null)
+ node.put("foundry", foundry);
+ }
+ return node;
+ }
+
+ @Override
+ public String path () {
+ return "query";
+ }
+
+
+ @Override
+ public JsonNode rewriteResult (KoralNode node) {
+ return null;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
new file mode 100644
index 0000000..1f0dad6
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
@@ -0,0 +1,41 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.ContextHolder;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 25/09/2015
+ */
+public class IdWriter implements RewriteTask.RewriteKoralToken {
+
+ private int counter;
+
+
+ public IdWriter () {
+ this.counter = 0;
+ }
+
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) {
+ if (node.get("@type").equals("koral:token")) {
+ String s = extractToken(node.rawNode());
+ if (s != null && !s.isEmpty())
+ node.put("idn", s + "_" + counter++);
+ }
+ return node;
+ }
+
+
+ private String extractToken (JsonNode token) {
+ JsonNode wrap = token.path("wrap");
+ if (!wrap.isMissingNode())
+ return wrap.path("key").asText();
+ return null;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java
new file mode 100644
index 0000000..a8870b7
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java
@@ -0,0 +1,301 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import java.util.*;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class KoralNode {
+ private JsonNode node;
+ private KoralRewriteBuilder rewrites;
+ private boolean remove;
+
+
+ public KoralNode (JsonNode node) {
+ this.node = node;
+ this.rewrites = new KoralRewriteBuilder();
+ this.remove = false;
+ }
+
+ public KoralNode (JsonNode node, KoralRewriteBuilder rewrites) {
+ this.node = node;
+ this.rewrites = rewrites;
+ this.remove = false;
+ }
+
+
+ public static KoralNode wrapNode (JsonNode node) {
+ return new KoralNode(node);
+ }
+
+ public void buildRewrites (JsonNode node) {
+ this.rewrites.build(node);
+ }
+
+
+ public void buildRewrites () {
+ this.rewrites.build(this.node);
+ }
+
+
+ @Override
+ public String toString () {
+ return this.node.toString();
+ }
+
+
+ public void put (String name, Object value) {
+ if (this.node.isObject() && this.node.path(name).isMissingNode()) {
+ ObjectNode node = (ObjectNode) this.node;
+ if (value instanceof String)
+ node.put(name, (String) value);
+ else if (value instanceof Integer)
+ node.put(name, (Integer) value);
+ else if (value instanceof JsonNode)
+ node.put(name, (JsonNode) value);
+ this.rewrites.add("injection", name);
+ }
+ else
+ throw new UnsupportedOperationException(
+ "node doesn't support this operation");
+ }
+
+
+ public void remove (Object identifier, RewriteIdentifier ident) {
+ boolean set = false;
+ if (this.node.isObject() && identifier instanceof String) {
+ ObjectNode n = (ObjectNode) this.node;
+ n.remove((String) identifier);
+ set = true;
+ }
+ else if (this.node.isArray() && identifier instanceof Integer) {
+ ArrayNode n = (ArrayNode) this.node;
+ n.remove((Integer) identifier);
+ set = true;
+ }
+
+ if (ident != null)
+ identifier = ident.toString();
+
+ if (set) {
+ this.rewrites.add("deletion", identifier);
+ }
+ }
+
+
+ public void replace (String name, Object value, RewriteIdentifier ident) {
+ if (this.node.isObject() && this.node.has(name)) {
+ ObjectNode n = (ObjectNode) this.node;
+ if (value instanceof String)
+ n.put(name, (String) value);
+ else if (value instanceof Integer)
+ n.put(name, (Integer) value);
+ else if (value instanceof JsonNode)
+ n.put(name, (JsonNode) value);
+
+ if (ident != null)
+ name = ident.toString();
+
+ this.rewrites.add("override", name);
+ }
+ }
+
+ public void replaceAt (String path, Object value, RewriteIdentifier ident) {
+ if (this.node.isObject() && !this.node.at(path).isMissingNode()) {
+ ObjectNode n = (ObjectNode) this.node.at(path);
+ n.removeAll();
+ n.putAll((ObjectNode)value);
+
+ String name = path;
+ if (ident != null)
+ name = ident.toString();
+
+ this.rewrites.add("override", name);
+ }
+ }
+
+ public void set (String name, Object value, RewriteIdentifier ident) {
+ if (this.node.isObject()) {
+ ObjectNode n = (ObjectNode) this.node;
+ if (value instanceof String)
+ n.put(name, (String) value);
+ else if (value instanceof Integer)
+ n.put(name, (Integer) value);
+ else if (value instanceof JsonNode)
+ n.put(name, (JsonNode) value);
+
+
+ if (ident != null)
+ name = ident.toString();
+
+ this.rewrites.add("insertion", name);
+ }
+ }
+
+ public void setAll (ObjectNode other) {
+ if (this.node.isObject()) {
+ ObjectNode n = (ObjectNode) this.node;
+ n.setAll(other);
+ }
+ this.rewrites.add("insertion",null);
+ }
+
+ public String get (String name) {
+ if (this.node.isObject())
+ return this.node.path(name).asText();
+ return null;
+ }
+
+
+ public KoralNode at (String name) {
+// this.node = this.node.at(name);
+// return this;
+ return new KoralNode(this.node.at(name), this.rewrites);
+ }
+
+
+ public boolean has (Object ident) {
+ if (ident instanceof String)
+ return this.node.has((String) ident);
+ else if (ident instanceof Integer)
+ return this.node.has((int) ident);
+ return false;
+ }
+
+
+ public JsonNode rawNode () {
+ return this.node;
+ }
+
+
+ public void removeNode (RewriteIdentifier ident) {
+ this.rewrites.add("deletion", ident.toString());
+ this.remove = true;
+ }
+
+ public static class RewriteIdentifier {
+
+ private String key, value;
+
+
+ public RewriteIdentifier (String key, Object value) {
+ this.key = key;
+ this.value = value.toString();
+ }
+
+
+ @Override
+ public String toString () {
+ return key + "(" + value + ")";
+ }
+
+
+ }
+
+
+ public boolean isRemove () {
+ return this.remove;
+ }
+
+
+ public static class KoralRewriteBuilder {
+
+ private List<KoralRewrite> rewrites;
+
+
+ public KoralRewriteBuilder () {
+ this.rewrites = new ArrayList<>();
+ }
+
+
+ public KoralRewriteBuilder add (String op, Object scope) {
+ KoralRewrite rewrite = new KoralRewrite();
+ rewrite.setOperation(op);
+ if (scope !=null){
+ rewrite.setScope(scope.toString());
+ }
+ this.rewrites.add(rewrite);
+ return this;
+ }
+
+
+ public JsonNode build (JsonNode node) {
+ for (KoralRewrite rewrite : this.rewrites) {
+ if (rewrite.map.get("operation") == null)
+ throw new UnsupportedOperationException(
+ "operation not set properly");
+
+ if (node.has("rewrites")) {
+ ArrayNode n = (ArrayNode) node.path("rewrites");
+ n.add(JsonUtils.valueToTree(rewrite.map));
+ }
+ else if (node.isObject()) {
+ ObjectNode n = (ObjectNode) node;
+ List l = new LinkedList<>();
+ l.add(JsonUtils.valueToTree(rewrite.map));
+ n.put("rewrites", JsonUtils.valueToTree(l));
+ }
+ else {
+ //fixme: matches in result will land here. rewrites need to be placed under root node - though then there might be unclear where they belong to
+ }
+
+ }
+ this.rewrites.clear();
+ return node;
+ }
+
+ }
+
+
+
+ private static class KoralRewrite {
+
+ private Map<String, String> map;
+
+
+ private KoralRewrite () {
+ this.map = new LinkedHashMap<>();
+ this.map.put("@type", "koral:rewrite");
+ this.map.put("src", "Kustvakt");
+ }
+
+
+ public KoralRewrite setOperation (String op) {
+ if (!op.startsWith("operation:"))
+ op = "operation:" + op;
+ this.map.put("operation", op);
+ return this;
+ }
+
+
+ public KoralRewrite setScope (String scope) {
+ this.map.put("scope", scope);
+ return this;
+ }
+
+ }
+
+
+ public boolean isMissingNode (String string) {
+ return this.node.at(string).isMissingNode();
+ }
+
+
+ public int size () {
+ return this.node.size();
+ }
+
+
+ public KoralNode get (int i) {
+// this.node = this.node.get(i);
+ return this.wrapNode(this.node.get(i));
+ }
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
new file mode 100644
index 0000000..29360d9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+
+/** EM:
+ * <ul>
+ * <li> Added default morphology foundry </li>
+ * <li> Made this class as a spring component</li>
+ * </ul>
+ * @author hanl, margaretha
+ * @date 14/10/2014
+ */
+@Component
+public class LayerMapper {
+
+ @Autowired
+ private KustvaktConfiguration config;
+
+ public String findFoundry (String layer) {
+ return findFoundry(layer, null);
+ }
+
+ /**
+ * find foundry entry in settings specific settings. Includes a
+ * call to #translateLayer to get the
+ * correct mapping for the layer denomination!
+ *
+ * @param layer
+ * @return
+ */
+
+ //todo: make mapping configurable!
+ public String findFoundry (String layer, UserSettingProcessor settings) {
+ if (settings != null) {
+ switch (translateLayer(layer.toLowerCase().trim())) {
+ case "d":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_RELATION);
+ case "c":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_CONSTITUENT);
+ case "pos":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_POS);
+ case "lemma":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_LEMMA);
+ case "surface":
+ return "opennlp";
+ // EM: added
+ case "s":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_STRUCTURE);
+ case "morphology":
+ return (String) settings
+ .get(Attributes.DEFAULT_FOUNDRY_MORPHOLOGY);
+ default:
+ // if the layer is not in this specific listing, assume a default layer
+ // like orth or other tokenization layers
+ return null;
+ }
+ }
+ else {
+ switch (translateLayer(layer.toLowerCase().trim())) {
+ case "d":
+ return config.getDefault_dep();
+ case "c":
+ return config.getDefault_const();
+ case "pos":
+ return config.getDefault_pos();
+ case "lemma":
+ return config.getDefault_lemma();
+ case "surface":
+ return config.getDefault_orthography();
+ // refers to "structure" and is used for paragraphs or sentence boundaries
+ case "s":
+ return config.getDefaultStructureFoundry();
+ //EM: added
+ case "morphology":
+ return config.getDefault_morphology();
+ default:
+ // if the layer is not in this specific listing, assume a default layer
+ // like orth or other tokenization layers
+ return null;
+ }
+ }
+ }
+
+
+ // relevance: map to access control id references. p is usually mapped to pos, l to lemma, etc.
+ public String translateLayer (String layer) {
+ switch (layer.toLowerCase().trim()) {
+ // case "pos":
+ // return "p";
+ // case "lemma":
+ // return "l";
+ case "m":
+ return "morphology"; // EM: changed msd to morphology
+// return "msd";
+ //todo the orth layer does not need a foundry entry
+ case "orth":
+ return "surface";
+ // EM: layer t does not exist at all
+// case "t":
+// return "surface";
+ // EM: this islegacy support
+ case "const":
+ return "c";
+ case "p":
+ return "pos";
+ case "l":
+ return "lemma";
+ default:
+ return layer;
+ }
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
new file mode 100644
index 0000000..a9380b9
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
@@ -0,0 +1,39 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 04/07/2015
+ */
+public class MetaConstraint implements RewriteTask.RewriteNodeAt {
+
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) {
+ // redundant
+ if (node.rawNode().has("meta")) {
+ JsonNode meta = node.rawNode().path("meta");
+ //todo: check meta parameter
+ System.out.println("HAVE TO CHECK THE META ENTRIES");
+ }
+ return node;
+ }
+
+
+ @Override
+ public JsonNode rewriteResult (KoralNode node) {
+ return null;
+ }
+
+
+ @Override
+ public String at () {
+ return "/meta";
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
new file mode 100644
index 0000000..d674ba6
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
@@ -0,0 +1,309 @@
+package de.ids_mannheim.korap.rewrite;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+// todo: do post processing!
+//todo: load rewritenode and rewritequery automatically from classpath by default, but namespaced from package
+public class RewriteHandler{
+ //implements BeanInjectable {
+
+ private static Logger jlog = LogManager.getLogger(RewriteHandler.class);
+ private Collection<RewriteTask.IterableRewritePath> node_processors;
+ private Collection<RewriteTask.RewriteKoralToken> token_node_processors;
+ private Collection<RewriteTask> query_processors;
+
+ private Set<Class> failed_task_registration;
+ @Autowired
+ private KustvaktConfiguration config;
+
+ public RewriteHandler (List<RewriteTask> rewriteTasks) {
+ this();
+ for (RewriteTask t : rewriteTasks){
+ addProcessor(t);
+ }
+ }
+
+ // EM: for testing
+ public RewriteHandler (KustvaktConfiguration config) {
+ this();
+ this.config=config;
+ }
+
+ public RewriteHandler () {
+ this.node_processors = new HashSet<>();
+ this.token_node_processors = new HashSet<>();
+ this.query_processors = new LinkedHashSet<>();
+ this.failed_task_registration = new HashSet<>();
+ }
+
+ public Set getFailedProcessors () {
+ return this.failed_task_registration;
+ }
+
+
+ public boolean addProcessor (RewriteTask rewriter) {
+ if (rewriter instanceof RewriteTask.RewriteKoralToken)
+ return this.token_node_processors
+ .add((RewriteTask.RewriteKoralToken) rewriter);
+ else if (rewriter instanceof RewriteTask.IterableRewritePath)
+ return this.node_processors
+ .add((RewriteTask.IterableRewritePath) rewriter);
+ else if (rewriter instanceof RewriteTask.RewriteQuery
+ | rewriter instanceof RewriteTask.RewriteResult)
+ return this.query_processors.add(rewriter);
+
+ this.failed_task_registration.add(rewriter.getClass());
+ return false;
+ }
+
+
+ @Override
+ public String toString () {
+ StringBuilder b = new StringBuilder();
+ b.append("--------------------------");
+ b.append("pre/post: " + this.node_processors.toString()).append("\n")
+ .append("\n")
+ .append("query: " + this.query_processors.toString())
+ .append("\n")
+ .append("koraltoken: " + this.token_node_processors.toString());
+ b.append("---------------------------");
+ return b.toString();
+ }
+
+
+ /**
+ * expects extended RewriteNode/Query class with empty default
+ * constructor
+ *
+ * @param rewriter
+ * @return boolean if rewriter class was successfully added to
+ * rewrite handler!
+ */
+ @Deprecated
+ public boolean add (Class<? extends RewriteTask> rewriter) {
+ RewriteTask task;
+ try {
+ Constructor c = rewriter.getConstructor();
+ task = (RewriteTask) c.newInstance();
+ }
+ catch (NoSuchMethodException | InvocationTargetException
+ | IllegalAccessException | InstantiationException e) {
+ this.failed_task_registration.add(rewriter);
+ return false;
+ }
+ return addProcessor(task);
+ }
+
+
+
+ public String processQuery (JsonNode root, User user)
+ throws KustvaktException {
+ RewriteProcess process = new RewriteProcess(root, user);
+ JsonNode pre = process.start(false);
+ return JsonUtils.toJSON(pre);
+ }
+
+
+ public String processQuery (String json, User user)
+ throws KustvaktException {
+ return processQuery(JsonUtils.readTree(json), user);
+ }
+
+
+ public String processResult (String json, User user)
+ throws KustvaktException {
+ return processResult(JsonUtils.readTree(json), user);
+ }
+
+
+ public String processResult (JsonNode node, User user)
+ throws KustvaktException {
+ RewriteProcess process = new RewriteProcess(node, user);
+ JsonNode pre = process.start(true);
+ return JsonUtils.toJSON(pre);
+ }
+
+
+ public void clear () {
+ this.node_processors.clear();
+ this.query_processors.clear();
+ this.token_node_processors.clear();
+ }
+
+
+// public <T extends ContextHolder> void insertBeans (T beans) {
+// this.beans = beans;
+// this.config = beans.getConfiguration();
+// }
+
+
+
+ public class RewriteProcess {
+
+ private static final boolean DEBUG = false;
+ private JsonNode root;
+ private User user;
+
+
+ private RewriteProcess (JsonNode root, User user) {
+ this.root = root;
+ this.user = user;
+ }
+
+
+ private KoralNode processNode (String key, JsonNode value,
+ boolean result) throws KustvaktException {
+ KoralNode kroot = KoralNode.wrapNode(value);
+ if (value.isObject()) {
+ if (value.has("operands")) {
+ JsonNode ops = value.at("/operands");
+ Iterator<JsonNode> it = ops.elements();
+ while (it.hasNext()) {
+ JsonNode next = it.next();
+ KoralNode kn = processNode(key, next, result);
+ if (kn.isRemove())
+ it.remove();
+ }
+ }
+ else if (value.path("@type").asText().equals("koral:token")) {
+ // todo: koral:token nodes cannot be flagged for deletion --> creates the possibility for empty koral:token nodes
+ rewrite(key, kroot,
+ RewriteHandler.this.token_node_processors, result);
+ return processNode(key, value.path("wrap"), result);
+ }
+ else {
+ return rewrite(key, kroot,
+ RewriteHandler.this.node_processors, result);
+ }
+ }
+ else if (value.isArray()) {
+ Iterator<JsonNode> it = value.elements();
+ while (it.hasNext()) {
+ JsonNode next = it.next();
+ KoralNode kn = processNode(key, next, result);
+ if (kn.isRemove())
+ it.remove();
+ }
+ }
+ return kroot;
+ }
+
+
+ private JsonNode start (boolean result) throws KustvaktException {
+ if (DEBUG){
+ jlog.debug("Running rewrite process on query "+ root);
+ }
+ if (root != null) {
+ Iterator<Map.Entry<String, JsonNode>> it = root.fields();
+ while (it.hasNext()) {
+ Map.Entry<String, JsonNode> next = it.next();
+ processNode(next.getKey(), next.getValue(), result);
+ }
+ processFixedNode(root, RewriteHandler.this.query_processors,
+ result);
+ }
+ return root;
+ }
+
+
+ /**
+ * @param node
+ * @param tasks
+ * @return boolean true if node is to be removed from parent!
+ * Only
+ * applies if parent is an array node
+ */
+ private KoralNode rewrite (String rootNode, KoralNode node,
+ Collection<? extends RewriteTask> tasks, boolean result)
+ throws KustvaktException {
+ if (RewriteHandler.this.config == null)
+ throw new RuntimeException("KustvaktConfiguration must be set!");
+
+ for (RewriteTask task : tasks) {
+ if (DEBUG) {
+ jlog.debug("running processor on node: " + node);
+ jlog.debug("on processor: " + task.getClass().toString());
+ }
+
+// if (RewriteHandler.this.beans != null
+// && task instanceof BeanInjectable)
+// ((BeanInjectable) task)
+// .insertBeans(RewriteHandler.this.beans);
+
+ if (task instanceof RewriteTask.IterableRewritePath) {
+ RewriteTask.IterableRewritePath rw = (RewriteTask.IterableRewritePath) task;
+ if (rw.path() != null && !rw.path().equals(rootNode)) {
+ if (DEBUG){
+ jlog.debug("skipping node: " + node);
+ }
+ continue;
+ }
+ }
+ if (!result && task instanceof RewriteTask.RewriteQuery) {
+ ((RewriteTask.RewriteQuery) task).rewriteQuery(node,
+ RewriteHandler.this.config, this.user);
+ }
+ else if (task instanceof RewriteTask.RewriteResult) {
+ ((RewriteTask.RewriteResult) task).rewriteResult(node);
+ }
+
+ if (node.isRemove()) {
+ node.buildRewrites(this.root.at("/" + rootNode));
+ break;
+ }
+ else
+ node.buildRewrites();
+ }
+ return node;
+ }
+
+
+ // fixme: merge with processNode!
+ private void processFixedNode (JsonNode node,
+ Collection<RewriteTask> tasks, boolean post)
+ throws KustvaktException {
+ for (RewriteTask task : tasks) {
+ KoralNode next = KoralNode.wrapNode(node);
+ if (task instanceof RewriteTask.RewriteNodeAt) {
+ RewriteTask.RewriteNodeAt rwa = (RewriteTask.RewriteNodeAt) task;
+ if ((rwa.at() != null && !node.at(rwa.at()).isMissingNode()))
+ next = next.at(rwa.at());
+ }
+
+ if (!post & task instanceof RewriteTask.RewriteQuery)
+ next = ((RewriteTask.RewriteQuery) task).rewriteQuery(next,
+ RewriteHandler.this.config, user);
+ else if (task instanceof RewriteTask.RewriteResult)
+ ((RewriteTask.RewriteResult) task).rewriteResult(next);
+ next.buildRewrites();
+
+ }
+ }
+
+
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
new file mode 100644
index 0000000..79065a5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
@@ -0,0 +1,75 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+public interface RewriteTask {
+
+
+ /**
+ * unspecified query rewrite that gets injected the entire root
+ * node during preprocessing
+ */
+ interface RewriteQuery extends RewriteTask {
+ /**
+ * @param node
+ * Json node in KoralNode wrapper
+ * @param config
+ * {@link KustvaktConfiguration} singleton instance
+ * to use default configuration parameters
+ * @param user
+ * injected by rewrite handler if available. Might
+ * cause {@link NullPointerException} if not
+ * checked properly
+ * @return
+ */
+ KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) throws KustvaktException;
+
+ }
+
+ /**
+ * Post processor targeted at result sets for queries
+ * {@link RewriteResult} queries will run
+ * after {@link IterableRewritePath} have been processed
+ */
+ interface RewriteResult extends RewriteTask {
+ JsonNode rewriteResult (KoralNode node) throws KustvaktException;
+ }
+
+ /**
+ * nodes subject to rewrites at fixed json pointer location.
+ * Json-pointer based rewrites are processed after iterable
+ * rewrites
+ * Deletion via KoralNode not allowed. Supports pre- and
+ * post-processing
+ */
+ interface RewriteNodeAt extends RewriteQuery, RewriteResult {
+ String at ();
+ }
+
+ /**
+ * terminal object nodes that are subject to rewrites through node
+ * iteration
+ * (both object and array node iteration supported)
+ */
+ interface IterableRewritePath extends RewriteQuery, RewriteResult {
+ String path ();
+ }
+
+ /**
+ * koral token nodes that are subject to rewrites
+ * Be aware that node rewrites are processed before query
+ * rewrites. Thus query rewrite may override previous node
+ * rewrites {@link RewriteKoralToken} rewrite DOES NOT support the
+ * deletion of the respective node
+ */
+ interface RewriteKoralToken extends RewriteQuery {}
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
new file mode 100644
index 0000000..1315c7a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
@@ -0,0 +1,81 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.config.BeanInjectable;
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.user.User;
+
+/**
+ * #ELEM(W ANA=N)
+ * <p/>
+ * {
+ * "@context":
+ * "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
+ * "errors": [],
+ * "warnings": [],
+ * "messages": [],
+ * "collection": {},
+ * "query": {
+ * "@type": "koral:span",
+ * "key": "w",
+ * "attr": {
+ * "@type": "koral:term",
+ * "layer": "p",
+ * "key": "N",
+ * "match": "match:eq"
+ * }
+ * },
+ * "meta": {}
+ * }
+ * <p/>
+ * <p/>
+ * email reference:
+ * Hallo Michael,
+ * mir fiel gestern bei der neuen KoralQuery Serialisierung noch ein
+ * Fall
+ * für default-Werte ein, die zumindest für viele Beispiele, die wir
+ * haben,
+ * relevant ist: Wenn ein koral:term in einem koral:span gewrappt ist,
+ * dann
+ * kann er eventuell nur einen Schlüssel haben ("s" oder "p" von "<s>"
+ * oder
+ * "<p>". In diesem Fall wäre der default layer "s" und die default
+ * foundry
+ * "base". (Im alten KoralQuery wurden spans nicht gewrappt - der Fall
+ * sollte aber erstmal weiter unterstützt werden.)
+ * Viele Grüße,
+ * Nils
+ *
+ * @author hanl
+ * @date 02/07/2015
+ */
+public class TreeConstraint implements RewriteTask.RewriteNodeAt {
+
+ private String pointer;
+
+
+ public TreeConstraint () {
+ super();
+ }
+
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) {
+ System.out.println("FIND PATH " + node.rawNode().findParent(pointer));
+
+ return node;
+ }
+
+
+ @Override
+ public JsonNode rewriteResult (KoralNode node) {
+ return null;
+ }
+
+
+ @Override
+ public String at () {
+ return null;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java b/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java
new file mode 100644
index 0000000..6d36199
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/security/context/KustvaktContext.java
@@ -0,0 +1,46 @@
+package de.ids_mannheim.korap.security.context;
+
+import javax.ws.rs.core.SecurityContext;
+
+import java.security.Principal;
+
+/**
+ * @author hanl
+ * @date 13/05/2014
+ *
+ * wrapper for REST security context
+ *
+ */
+public class KustvaktContext implements SecurityContext {
+
+ private TokenContext user;
+
+
+ public KustvaktContext (final TokenContext user) {
+ this.user = user;
+ }
+
+
+ @Override
+ public Principal getUserPrincipal () {
+ return this.user;
+ }
+
+
+ @Override
+ public boolean isUserInRole (String role) {
+ throw new UnsupportedOperationException();
+ }
+
+
+ @Override
+ public boolean isSecure () {
+ return false;
+ }
+
+
+ @Override
+ public String getAuthenticationScheme () {
+ return SecurityContext.BASIC_AUTH;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java b/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java
new file mode 100644
index 0000000..b31c643
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/security/context/TokenContext.java
@@ -0,0 +1,173 @@
+package de.ids_mannheim.korap.security.context;
+
+import java.io.Serializable;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * EM:
+ * - change datatype of tokenType from string to enum
+ * - added authenticationTime
+ *
+ * @author hanl
+ * @date 27/01/2014
+ */
+@Data
+public class TokenContext implements java.security.Principal, Serializable {
+
+ private ZonedDateTime authenticationTime;
+ /**
+ * session relevant data. Are never persisted into a database
+ */
+ private String username;
+ private long expirationTime;
+ // either "session_token " / "api_token
+ private TokenType tokenType;
+ private String token;
+ private boolean secureRequired;
+
+// @Getter(AccessLevel.PRIVATE)
+ @Setter(AccessLevel.PRIVATE)
+ private Map<String, Object> parameters;
+ private String hostAddress;
+ private String userAgent;
+
+
+ public TokenContext () {
+ this.parameters = new HashMap<>();
+ this.setUsername("");
+ this.setToken("");
+ this.setSecureRequired(false);
+ this.setExpirationTime(-1);
+ }
+
+
+ private Map statusMap () {
+ Map m = new HashMap();
+ if (username != null && !username.isEmpty())
+ m.put(Attributes.USERNAME, username);
+ m.put(Attributes.TOKEN_EXPIRATION,
+ TimeUtils.format(this.expirationTime));
+ m.put(Attributes.TOKEN, this.token);
+ m.put(Attributes.TOKEN_TYPE, this.tokenType);
+ return m;
+ }
+
+
+ public Map<String, Object> params () {
+ return new HashMap<>(parameters);
+ }
+
+ public boolean match (TokenContext other) {
+ if (other.getToken().equals(this.token))
+ if (this.getHostAddress().equals(this.hostAddress))
+ // user agent should be irrelvant -- what about os
+ // system version?
+ // if (other.getUserAgent().equals(this.userAgent))
+ return true;
+ return false;
+ }
+
+
+ public void addContextParameter (String key, String value) {
+ this.parameters.put(key, value);
+ }
+
+
+ public void addParams (Map<String, Object> map) {
+ for (Map.Entry<String, Object> e : map.entrySet())
+ this.parameters.put(e.getKey(), String.valueOf(e.getValue()));
+ }
+
+
+ public void removeContextParameter (String key) {
+ this.parameters.remove(key);
+ }
+
+
+ public void setExpirationTime (long date) {
+ this.expirationTime = date;
+ }
+
+
+ // todo: complete
+ public static TokenContext fromJSON (String s) throws KustvaktException {
+ JsonNode node = JsonUtils.readTree(s);
+ TokenContext c = new TokenContext();
+ if (node != null) {
+ c.setUsername(node.path(Attributes.USERNAME).asText());
+ c.setToken(node.path(Attributes.TOKEN).asText());
+ }
+ return c;
+ }
+
+
+ public static TokenContext fromOAuth2 (String s) throws KustvaktException {
+ JsonNode node = JsonUtils.readTree(s);
+ TokenContext c = new TokenContext();
+ if (node != null) {
+ c.setToken(node.path("token").asText());
+ c.setTokenType(TokenType.valueOf(node.path("token_type").asText()));
+ c.setExpirationTime(node.path("expires_in").asLong());
+ c.addContextParameter("refresh_token",
+ node.path("refresh_token").asText());
+
+ }
+ return c;
+ }
+
+
+ public boolean isValid () {
+ return (this.username != null && !this.username.isEmpty())
+ && (this.token != null && !this.token.isEmpty())
+ && (this.tokenType != null);
+ }
+
+
+ public String getToken () {
+ return token;
+ }
+
+
+ public String toJson () throws KustvaktException {
+ return JsonUtils.toJSON(this.statusMap());
+ }
+
+
+ public boolean isDemo () {
+ return User.UserFactory.isDemo(this.username);
+ }
+
+
+
+ @Override
+ public String getName () {
+ return this.getUsername();
+ }
+
+
+ public ZonedDateTime getAuthenticationTime () {
+ return authenticationTime;
+ }
+
+
+ public void setAuthenticationTime (ZonedDateTime authTime) {
+ this.authenticationTime = authTime;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
new file mode 100644
index 0000000..e7fa88e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/server/KustvaktBaseServer.java
@@ -0,0 +1,169 @@
+package de.ids_mannheim.korap.server;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
+import java.util.Scanner;
+
+import javax.servlet.ServletContextListener;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.server.handler.ShutdownHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.glassfish.jersey.server.ServerProperties;
+import org.glassfish.jersey.servlet.ServletContainer;
+import org.springframework.web.context.ContextLoaderListener;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.encryption.RandomCodeGenerator;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @author hanl
+ * @date 01/06/2015
+ */
+public abstract class KustvaktBaseServer {
+
+ protected static KustvaktConfiguration config;
+ protected static String springConfig = "default-config.xml";
+
+ protected static String rootPackages;
+ protected static KustvaktArgs kargs;
+
+ public KustvaktBaseServer () {
+ rootPackages = "de.ids_mannheim.korap.core.web; "
+ + "de.ids_mannheim.korap.web; "
+ + "com.fasterxml.jackson.jaxrs.json;";
+
+ File d = new File(KustvaktConfiguration.DATA_FOLDER);
+ if (!d.exists()) {
+ d.mkdir();
+ }
+ }
+
+ protected KustvaktArgs readAttributes (String[] args) {
+ KustvaktArgs kargs = new KustvaktArgs();
+ for (int i = 0; i < args.length; i++) {
+ switch ((args[i])) {
+ case "--spring-config":
+ kargs.setSpringConfig(args[i + 1]);
+ break;
+ case "--port":
+ kargs.setPort(Integer.valueOf(args[i + 1]));
+ break;
+ case "--help":
+ StringBuffer b = new StringBuffer();
+
+ b.append("Parameter description: \n")
+ .append("--spring-config <Spring XML configuration>\n")
+ .append("--port <Server port number>\n")
+ .append("--help : This help menu\n");
+ System.out.println(b.toString());
+ System.out.println();
+ return (KustvaktArgs) null;
+ }
+ }
+ return kargs;
+ }
+
+ protected void start ()
+ throws KustvaktException, IOException, NoSuchAlgorithmException {
+
+ if (kargs.port == -1) {
+ kargs.setPort(config.getPort());
+ }
+
+ String adminToken="";
+ File f = new File("adminToken");
+ if (!f.exists()) {
+ RandomCodeGenerator random = new RandomCodeGenerator();
+ adminToken = random.createRandomCode(config);
+ FileOutputStream fos = new FileOutputStream(new File("adminToken"));
+ OutputStreamWriter writer =
+ new OutputStreamWriter(fos, StandardCharsets.UTF_8.name());
+ writer.append("token=");
+ writer.append(adminToken);
+ writer.flush();
+ writer.close();
+ }
+ else {
+ Scanner scanner = new Scanner(f);
+ adminToken = scanner.nextLine().substring(6);
+ scanner.close();
+ }
+
+ Server server = new Server();
+
+ ServletContextHandler contextHandler =
+ new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
+ contextHandler.setContextPath("/");
+
+ if (kargs.getSpringConfig()!=null) {
+ contextHandler.setInitParameter("contextConfigLocation",
+ "file:" + kargs.getSpringConfig());
+ }
+ else {
+ contextHandler.setInitParameter("contextConfigLocation",
+ "classpath:" + this.springConfig);
+ }
+
+ ServletContextListener listener = new ContextLoaderListener();
+ contextHandler.addEventListener(listener);
+ contextHandler.setInitParameter("adminToken", adminToken);
+
+ ServletHolder servletHolder = new ServletHolder(new ServletContainer());
+ servletHolder.setInitParameter(
+ ServerProperties.PROVIDER_PACKAGES, rootPackages);
+ servletHolder.setInitOrder(1);
+ contextHandler.addServlet(servletHolder, config.getBaseURL());
+
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(kargs.port);
+ connector.setIdleTimeout(60000);
+ connector.getConnectionFactory(HttpConnectionFactory.class)
+ .getHttpConfiguration().setRequestHeaderSize(64000);
+
+ ShutdownHandler shutdownHandler = new ShutdownHandler(adminToken,true,true);
+
+ HandlerList handlers = new HandlerList();
+ handlers.addHandler(shutdownHandler);
+ handlers.addHandler(contextHandler);
+
+ server.setHandler(handlers);
+
+ server.setConnectors(new Connector[] { connector });
+ try {
+ server.start();
+ server.join();
+ }
+ catch (Exception e) {
+ System.out.println("Server could not be started!");
+ System.out.println(e.getMessage());
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+ @Setter
+ public static class KustvaktArgs {
+
+ @Getter
+ private String springConfig;
+ private int port;
+
+ public KustvaktArgs () {
+ this.port = -1;
+ this.springConfig = null;
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java b/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java
new file mode 100644
index 0000000..21aea9a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/DataFactory.java
@@ -0,0 +1,265 @@
+package de.ids_mannheim.korap.user;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.validator.Validator;
+
+/**
+ * EM: util class
+ *
+ * @author hanl, margaretha
+ * @date 27/01/2016
+ */
+public abstract class DataFactory {
+
+ private static DataFactory factory;
+
+ private DataFactory () {}
+
+ public static DataFactory getFactory () {
+ if (factory == null)
+ factory = new DefaultFactory();
+ return factory;
+ }
+
+
+ /**
+ * if data string null, returns an empty data holding object
+ *
+ * @param data
+ * @return
+ */
+ public abstract Object convertData (String data);
+
+
+ public abstract int size (Object data);
+
+
+ public abstract Set<String> keys (Object data);
+
+
+ public abstract Collection<Object> values (Object data);
+
+ public abstract Object validate(Object data, Validator validator) throws KustvaktException;
+
+ @Deprecated
+ public abstract Map<String, Object> fields (Object data);
+
+
+ public abstract Object getValue (Object data, String pointer);
+
+
+ public abstract boolean addValue (Object data, String field, Object value);
+
+
+ public abstract boolean removeValue (Object data, String field);
+
+
+ public abstract String toStringValue (Object data) throws KustvaktException;
+
+ public abstract Object filter(Object data, String ... keys);
+
+ public boolean checkDataType (Object data) {
+ throw new RuntimeException("Wrong data type for factory setting!");
+ }
+
+
+ /**
+ * updates data1 with values from data2
+ *
+ * @param data1
+ * data object that needs update
+ * @param data2
+ * values that update data1
+ * @return
+ */
+ public abstract Object merge (Object data1, Object data2);
+
+
+
+ private static class DefaultFactory extends DataFactory {
+
+ @Override
+ public Object convertData (String data) {
+ if (data == null)
+ return JsonUtils.createObjectNode();
+ try {
+ return JsonUtils.readTree(data);
+ }
+ catch (KustvaktException e) {
+ return null;
+ }
+ }
+
+
+ @Override
+ public int size (Object data) {
+ if (checkDataType(data))
+ return ((JsonNode) data).size();
+ return -1;
+ }
+
+
+ @Override
+ public Set<String> keys (Object data) {
+ Set<String> keys = new HashSet<>();
+ if (checkDataType(data) && ((JsonNode) data).isObject()) {
+ Iterator<String> it = ((JsonNode) data).fieldNames();
+ while (it.hasNext())
+ keys.add((String) it.next());
+ }
+ return keys;
+ }
+
+
+ @Override
+ public Collection<Object> values (Object data) {
+ return new HashSet<>();
+ }
+
+ @Override
+ public Object validate(Object data, Validator validator) throws KustvaktException {
+ if (checkDataType(data) && ((JsonNode) data).isObject()) {
+ try {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> mdata = JsonUtils.read(toStringValue(data), HashMap.class);
+ return validator.validateMap(mdata);
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ return JsonUtils.createObjectNode();
+ }
+
+
+ @Override
+ public Map<String, Object> fields (Object data) {
+ return new HashMap<>();
+ }
+
+
+ @Override
+ public Object getValue (Object data, String key) {
+ if (checkDataType(data)) {
+ JsonNode value;
+ if (key.startsWith("/"))
+ value = ((JsonNode) data).at(key);
+ else
+ value = ((JsonNode) data).path(key);
+
+ if (value.canConvertToInt())
+ return value.asInt();
+ else if (value.isBoolean())
+ return value.asBoolean();
+ else if (value.isTextual())
+ return value.asText();
+ }
+ return null;
+ }
+
+
+ //fixme: test that this works with different types
+ @Override
+ public boolean addValue (Object data, String field, Object value) {
+ if (checkDataType(data)) {
+ if (((JsonNode) data).isObject()) {
+ ObjectNode node = (ObjectNode) data;
+ if (value instanceof String)
+ node.put(field, (String) value);
+ if (value instanceof Boolean)
+ node.put(field, (Boolean) value);
+ if (value instanceof Integer)
+ node.put(field, (Integer) value);
+ if (value instanceof JsonNode)
+ node.set(field, (JsonNode) value);
+ // EM: added
+ if (value instanceof Collection<?>){
+ Collection<?> list = (Collection<?>) value;
+ ArrayNode arrayNode = JsonUtils.createArrayNode();
+ for (Object o : list){
+ addValue(arrayNode, null, o);
+ }
+ node.set(field,arrayNode);
+ }
+ return true;
+ }
+ else if (((JsonNode) data).isArray()) {
+ ArrayNode node = (ArrayNode) data;
+ if (value instanceof String)
+ node.add((String) value);
+ if (value instanceof Boolean)
+ node.add((Boolean) value);
+ if (value instanceof Integer)
+ node.add((Integer) value);
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ @Override
+ public boolean removeValue (Object data, String field) {
+ if (checkDataType(data) && ((JsonNode) data).isObject()) {
+ ObjectNode node = (ObjectNode) data;
+ node.remove(field);
+ return true;
+ }
+ return false;
+ }
+
+
+ @Override
+ public String toStringValue (Object data) throws KustvaktException {
+ if (data instanceof JsonNode)
+ return JsonUtils.toJSON(data);
+ return data.toString();
+ }
+
+ @Override
+ public Object filter(Object data, String... keys) {
+ if (checkDataType(data) && ((JsonNode) data).isObject()) {
+ ObjectNode node = ((JsonNode) data).deepCopy();
+ return node.retain(keys);
+ }
+ return JsonUtils.createObjectNode();
+ }
+
+
+ @Override
+ public boolean checkDataType (Object data) {
+ if (!(data instanceof JsonNode))
+ super.checkDataType(data);
+ return true;
+ }
+
+
+ @Override
+ public Object merge (Object data1, Object data2) {
+ if (checkDataType(data1) && checkDataType(data2)) {
+ if (((JsonNode) data1).isObject()
+ && ((JsonNode) data2).isObject()) {
+ ((ObjectNode) data1).setAll((ObjectNode) data2);
+ }
+ else if (((JsonNode) data1).isArray()
+ && ((JsonNode) data2).isArray()) {
+ ((ArrayNode) data1).addAll((ArrayNode) data2);
+ }
+ }
+ return data1;
+ }
+
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java b/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java
new file mode 100644
index 0000000..5a48bcb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/DemoUser.java
@@ -0,0 +1,29 @@
+package de.ids_mannheim.korap.user;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+@Getter
+@Setter
+public class DemoUser extends User implements Serializable {
+ private static final long serialVersionUID = -5015206272520970500L;
+ public static final String DEMOUSER_NAME = "guest";
+ public static final Integer DEMOUSER_ID = 1654234534;
+ private static final long ACCOUNT_CREATED = 1377102171202L;
+ public static final String PASSPHRASE = "demo";
+
+
+ protected DemoUser () {
+ super(DEMOUSER_NAME, 2);
+ this.setId(-1);
+ this.setAccountCreation(ACCOUNT_CREATED);
+ }
+
+
+ protected User clone () {
+ return new DemoUser();
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java b/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java
new file mode 100644
index 0000000..cf254dd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/GenericUserData.java
@@ -0,0 +1,24 @@
+package de.ids_mannheim.korap.user;
+
+/**
+ * Created by hanl on 07.06.16.
+ */
+public class GenericUserData extends Userdata {
+
+
+ public GenericUserData () {
+ super(-1);
+ }
+
+
+ @Override
+ public String[] requiredFields () {
+ return new String[0];
+ }
+
+
+ @Override
+ public String[] defaultFields () {
+ return new String[0];
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java b/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
new file mode 100644
index 0000000..8f51378
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/KorAPUser.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.user;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class KorAPUser extends User {
+ private static Logger jlog = LogManager.getLogger(KorAPUser.class);
+ private static final long serialVersionUID = -7108308497625884584L;
+
+ //fixme: accountlink to shibboleth account
+ private String accountLink;
+
+ private String password;
+ private String URIFragment;
+ private Long URIExpiration;
+
+
+ public KorAPUser (String username) {
+ super(username, 0);
+ this.URIFragment = "";
+ this.URIExpiration = 0L;
+ }
+
+
+ public KorAPUser (Integer id, String username) {
+ this(username);
+ this.setId(id);
+ }
+
+
+ public KorAPUser () {
+ super();
+ }
+
+
+ @Override
+ protected User clone () {
+ KorAPUser user = new KorAPUser(this.getUsername());
+ user.setUsername(this.getUsername());
+ user.setAccountCreation(this.getAccountCreation());
+ return user;
+ }
+
+
+ @Override
+ public int hashCode () {
+ int result = super.hashCode();
+ result = 31 * result + (jlog != null ? jlog.hashCode() : 0);
+ result = 31 * result + (password != null ? password.hashCode() : 0);
+ result = 31 * result
+ + (URIFragment != null ? URIFragment.hashCode() : 0);
+ result = 31 * result
+ + (URIExpiration != null ? URIExpiration.hashCode() : 0);
+ return result;
+ }
+
+
+ @Override
+ public boolean equals (Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof KorAPUser))
+ return false;
+ if (!super.equals(o))
+ return false;
+
+ KorAPUser korAPUser = (KorAPUser) o;
+ if (URIExpiration != korAPUser.URIExpiration)
+ return false;
+ if (URIFragment != null ? !URIFragment.equals(korAPUser.URIFragment)
+ : korAPUser.URIFragment != null)
+ return false;
+ return true;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/User.java b/full/src/main/java/de/ids_mannheim/korap/user/User.java
new file mode 100644
index 0000000..eb6cf53
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/User.java
@@ -0,0 +1,298 @@
+package de.ids_mannheim.korap.user;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.ParamFields;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import de.ids_mannheim.korap.web.utils.KustvaktMap;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+
+@Data
+public abstract class User implements Serializable {
+
+ //EM: add
+ private String email;
+ //EM: finish
+
+ private Integer id;
+ // in local its username, in shib it's edupersonPrincipalName
+ private String username;
+ private Long accountCreation;
+ private boolean isAccountLocked;
+ private int type;
+ private ParamFields fields;
+ @Getter(AccessLevel.PRIVATE)
+ @Setter(AccessLevel.PRIVATE)
+ private UserSettingProcessor settings;
+ //todo: remove!
+ @Getter(AccessLevel.PRIVATE)
+ @Setter(AccessLevel.PRIVATE)
+ private UserDetails details;
+ @Getter(AccessLevel.PRIVATE)
+ @Setter(AccessLevel.PRIVATE)
+ private List<UserQuery> queries;
+
+ private UserSettingProcessor userSettingProcessor;
+
+// private boolean isSystemAdmin;
+
+ // Values for corpusAccess:
+ public enum CorpusAccess {
+ FREE, // Access to licence free corpora only, without login
+ PUB, // Access to public (= öffentliche Korpora) only, externes Login.
+ ALL // Access to all corpora, internes Login.
+ };
+
+ @Getter
+ @Setter
+ private CorpusAccess corpusAccess = CorpusAccess.FREE;
+
+ // values for location (set using the X-forwarded-for Header):
+ public enum Location {
+ INTERN, // KorAP accessed by internal Client (inside intranet).
+ EXTERN // KorAP accessed by external Client (outside intranet).
+ };
+
+ @Getter
+ @Setter
+ private Location location = Location.EXTERN;
+
+
+ protected User () {
+ this.fields = new ParamFields();
+ this.accountCreation = TimeUtils.getNow().getMillis();
+ this.isAccountLocked = false;
+ this.username = "";
+ this.id = -1;
+ this.location = Location.EXTERN;
+ this.corpusAccess = CorpusAccess.FREE;
+ }
+
+
+ protected User (int type) {
+ this();
+ this.type = type;
+ }
+
+
+ protected User (String username, int type) {
+ this(type);
+ this.username = username;
+ }
+
+
+ public void addField (ParamFields.Param param) {
+ this.fields.add(param);
+ }
+
+
+ public <T extends ParamFields.Param> T getField (Class<T> cl) {
+ return this.fields.get(cl);
+ }
+
+ public void setId (Integer id) {
+ this.id = id;
+ // if (this.settings != null)
+ // this.settings.setUserID(this.id);
+ // if (this.details != null)
+ // this.details.setUserID(this.id);
+ }
+
+
+ public Map<String, Object> toMap () {
+ Map map = new HashMap();
+ map.put(Attributes.USERNAME, this.username);
+ //TimeUtils.format(new DateTime(this.accountCreation))
+ map.put(Attributes.ACCOUNT_CREATION, this.accountCreation);
+
+ // if (this.getDetails() != null)
+ // map.putAll(this.getDetails().toMap());
+ return map;
+ }
+
+
+ public Map toCache () {
+ Map map = new HashMap();
+ map.put(Attributes.ID, this.id);
+ map.put(Attributes.USERNAME, this.username);
+ map.put(Attributes.ACCOUNT_CREATION,
+ TimeUtils.format(new DateTime(this.accountCreation)));
+ map.put(Attributes.ACCOUNTLOCK, this.isAccountLocked);
+ map.put(Attributes.TYPE, this.type);
+ return map;
+ }
+
+
+ @Override
+ public boolean equals (Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof User))
+ return false;
+ User user = (User) o;
+ if (!username.equals(user.username))
+ return false;
+ return true;
+ }
+
+// public boolean isAdmin () {
+// return this.getUsername().equals(ADMINISTRATOR_ID);
+// }
+
+
+ protected abstract User clone ();
+
+
+ @Override
+ public String toString () {
+ final StringBuffer sb = new StringBuffer();
+ sb.append("id='").append(id).append('\'');
+ sb.append(", username='").append(username).append('\'');
+ return sb.toString();
+ }
+
+ public String locationtoString()
+
+ {
+ if( this.location == Location.INTERN)
+ return "INTERN";
+ else if( this.location == Location.EXTERN )
+ return "EXTERN";
+ else
+ return "???";
+ }
+
+ public String accesstoString()
+
+ {
+ if( this.corpusAccess == CorpusAccess.ALL )
+ return "ALL";
+ else if( this.corpusAccess == CorpusAccess.PUB )
+ return "PUB";
+ else if( this.corpusAccess == CorpusAccess.FREE )
+ return "FREE";
+ else
+ return "???";
+ }
+
+ public static class UserFactory {
+
+ public static KorAPUser getUser (String username) {
+ return new KorAPUser(username);
+ }
+
+
+ public static KorAPUser getUser (String username, String password) {
+ KorAPUser user = new KorAPUser(username);
+ user.setPassword(password);
+ return user;
+ }
+
+// public static KorAPUser getAdmin () {
+// return new KorAPUser(ADMINISTRATOR_ID, ADMINISTRATOR_NAME);
+// }
+
+
+ public static DemoUser getDemoUser () {
+ return new DemoUser();
+ }
+
+
+ public static DemoUser getDemoUser (Integer id) {
+ DemoUser demo = new DemoUser();
+ demo.setId(id);
+ return demo;
+ }
+
+
+ public static boolean isDemo (String username) {
+ return new DemoUser().getUsername().equalsIgnoreCase(username);
+ }
+
+
+// public static ShibUser getShibInstance (String eduPersonID,
+// String mail, String cn) {
+// ShibUser u = new ShibUser(eduPersonID);
+// u.setAffiliation("");
+// u.setMail(mail);
+// u.setUsername(eduPersonID);
+// u.setCn(cn);
+// return u;
+// }
+
+
+ public static KorAPUser toKorAPUser (Map<String, Object> map) {
+ KorAPUser user = UserFactory.getUser((String) map
+ .get(Attributes.USERNAME));
+ user.setPassword((String) map.get(Attributes.PASSWORD));
+ int id = map.get(Attributes.ID) == null ? -1 : (int) map
+ .get(Attributes.ID);
+ if (id != -1)
+ user.setId(id);
+ long cr = map.get(Attributes.ACCOUNT_CREATION) == null ? -1
+ : (long) map.get(Attributes.ACCOUNT_CREATION);
+ if (cr != -1)
+ user.setAccountCreation((Long) map
+ .get(Attributes.ACCOUNT_CREATION));
+ return user;
+ }
+
+
+ public static User toUser (Map<String, Object> map) {
+ KustvaktMap kmap = new KustvaktMap(map);
+ int type = map.get(Attributes.TYPE) == null ? 0 : (Integer) kmap
+ .get(Attributes.TYPE, Integer.class);
+ User user;
+ long created = -1;
+ int id = kmap.get(Attributes.ID, Integer.class) == null ? -1
+ : (Integer) kmap.get(Attributes.ID, Integer.class);
+
+ if (map.get(Attributes.ACCOUNT_CREATION) != null)
+ created = TimeUtils.getTime(kmap.get(Attributes.ACCOUNT_CREATION))
+ .getMillis();
+ switch (type) {
+ case 0:
+ user = UserFactory.getUser(kmap.get(Attributes.USERNAME));
+ if (id != -1)
+ user.setId((Integer) kmap.get(Attributes.ID,
+ Integer.class));
+ user.setAccountLocked(map.get(Attributes.ACCOUNTLOCK) == null ? false
+ : (Boolean) kmap.get(Attributes.ACCOUNTLOCK,
+ Boolean.class));
+ user.setAccountCreation(created);
+ break;
+ default:
+ user = UserFactory.getDemoUser();
+ user.setAccountCreation(created);
+ }
+ return user;
+ }
+
+
+ public static KorAPUser toUser (String value) throws KustvaktException {
+ JsonNode node = JsonUtils.readTree(value);
+ KorAPUser user = UserFactory.getUser(node.path(Attributes.USERNAME)
+ .asText());
+ user.setAccountLocked(node.path(Attributes.ACCOUNTLOCK).asBoolean());
+ user.setAccountLink(node.path(Attributes.ACCOUNTLINK).asText());
+ user.setAccountCreation(node.path(Attributes.ACCOUNT_CREATION)
+ .asLong());
+ user.setPassword(node.path(Attributes.PASSWORD).asText());
+ return user;
+ }
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java b/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java
new file mode 100644
index 0000000..9901592
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserDetails.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+/**
+ * @author hanl
+ * @date 22/01/2016
+ * persistence issue with query request
+ */
+public class UserDetails extends Userdata {
+
+
+ public UserDetails() {
+
+ }
+
+ public UserDetails(Integer userid) {
+ super(userid);
+ }
+
+ //todo: make configurable!
+ @Override
+ public String[] requiredFields () {
+ return new String[] { Attributes.EMAIL, Attributes.ADDRESS,
+ Attributes.LASTNAME, Attributes.FIRSTNAME };
+ }
+
+
+ @Override
+ public String[] defaultFields () {
+ return new String[] { Attributes.EMAIL, Attributes.ADDRESS,
+ Attributes.LASTNAME, Attributes.FIRSTNAME, Attributes.PHONE,
+ Attributes.COUNTRY, Attributes.INSTITUTION, Attributes.GENDER };
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java b/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java
new file mode 100644
index 0000000..ad48a2d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserQuery.java
@@ -0,0 +1,143 @@
+package de.ids_mannheim.korap.user;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * User: hanl
+ * Date: 9/16/13
+ * Time: 4:38 PM
+ */
+@Data
+public class UserQuery {
+
+ private Integer id;
+ private String queryLanguage;
+ private String query;
+ private String name;
+ private String description;
+ private Integer creator;
+
+
+ public UserQuery (Integer id, int creator) {
+ setId(id);
+ setCreator(creator);
+ setName("");
+ setDescription("");
+ setQuery("");
+ setQueryLanguage("");
+ }
+
+
+ public UserQuery (String ql, String query, String description) {
+ setDescription(description);
+ setQuery(query);
+ setQueryLanguage(ql);
+ }
+
+
+ public UserQuery () {
+ setDescription("");
+ setQuery("");
+ setQueryLanguage("");
+ setName("");
+ }
+
+
+ public void setQuery (String query) {
+ this.query = query;
+ setName("Query: "
+ + query.substring(0, query.length() > 20 ? 20 : query.length()));
+ }
+
+
+ // todo: use example queries or store in database
+ public static List<UserQuery> demoUserQueries () {
+
+ List<UserQuery> queries = new ArrayList<>();
+ UserQuery q1 = new UserQuery();
+ q1.setQueryLanguage("COSMAS2");
+ q1.setQuery("$wegen #IN(L) <s>");
+ q1.setDescription("Findet 'wegen' an Satzanfängen. Berücksichtigt auch Groß- und Kleinschreibung");
+
+ //todo: change query
+ UserQuery q2 = new UserQuery();
+ q2.setQueryLanguage("COSMAS2");
+ q2.setQuery("base/cons:Buchstabe base/aggr:Buchstabe");
+
+ UserQuery q3 = new UserQuery();
+ q3.setQueryLanguage("COSMAS2");
+ q3.setDescription("Regular Expression Search");
+ q3.setQuery("id:/WPD_AAA.*/ AND textClass:sport");
+
+ UserQuery q4 = new UserQuery();
+ q4.setQueryLanguage("COSMAS2");
+ q4.setQuery("mpt/syntax_pos:@CC\\|und");
+
+ UserQuery q5 = new UserQuery();
+ q5.setQueryLanguage("COSMAS2");
+ q5.setQuery("VVINF\\|.*en");
+
+ queries.add(q1);
+ // queries.add(q2);
+ // queries.add(q3);
+ queries.add(q4);
+ queries.add(q5);
+ return queries;
+ }
+
+
+ //id is irrevelant, since data was coming
+ // from frontend and thus this object does not contain a id that could be compared!
+ // same with the userAccount. Not set yet!
+ @Override
+ public boolean equals (Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof UserQuery))
+ return false;
+ UserQuery userQuery = (UserQuery) o;
+ if (!query.equals(userQuery.query))
+ return false;
+ if (!queryLanguage.equals(userQuery.queryLanguage))
+ return false;
+ return true;
+ }
+
+
+ @Override
+ public int hashCode () {
+ int result = getId() != null ? getId().hashCode() : 0;
+ result = 31 * result
+ + (queryLanguage != null ? queryLanguage.hashCode() : 0);
+ result = 31 * result + (query != null ? query.hashCode() : 0);
+ return result;
+ }
+
+
+ @Override
+ public String toString () {
+ final StringBuffer sb = new StringBuffer("UserQuery{");
+ sb.append("id=").append(getId());
+ // sb.append(", owner=").append(getOwner());
+ sb.append(", queryLanguage='").append(queryLanguage).append('\'');
+ sb.append(", query='").append(query).append('\'');
+ sb.append(", description='").append(getDescription()).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+
+
+ public Map toMap () {
+ Map map = new HashMap();
+ map.put("name", this.name);
+ map.put("description", this.description);
+ map.put("query", this.query);
+ map.put("queryLanguage", this.queryLanguage);
+ return map;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java b/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java
new file mode 100644
index 0000000..57b5e4b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/UserSettingProcessor.java
@@ -0,0 +1,39 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+/**
+ * @author hanl, margaretha
+ * @date 28/01/2016
+ */
+public class UserSettingProcessor extends Userdata {
+
+ public UserSettingProcessor() {
+
+ }
+
+ @Deprecated
+ public UserSettingProcessor(Integer userid) {
+ super(userid);
+ }
+
+ // EM: added
+ public UserSettingProcessor(String data) {
+ super(data);
+ }
+
+ @Override
+ public String[] requiredFields () {
+ return new String[] {};
+ }
+
+
+ @Override
+ public String[] defaultFields () {
+ return new String[] { Attributes.DEFAULT_FOUNDRY_RELATION,
+ Attributes.DEFAULT_FOUNDRY_POS,
+ Attributes.DEFAULT_FOUNDRY_CONSTITUENT,
+ Attributes.DEFAULT_FOUNDRY_LEMMA, Attributes.QUERY_LANGUAGE,
+ Attributes.PAGE_LENGTH };
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java b/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java
new file mode 100644
index 0000000..e735776
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/user/Userdata.java
@@ -0,0 +1,167 @@
+package de.ids_mannheim.korap.user;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.validator.Validator;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.*;
+
+/**
+ * @author hanl, margaretha
+ * @date 22/01/2016
+ *
+ */
+public abstract class Userdata {
+
+ public DataFactory dataFactory = DataFactory.getFactory();
+
+ @Deprecated
+ @Getter
+ @Setter
+ private Integer id;
+ @Getter(AccessLevel.PRIVATE)
+ private Object data;
+ @Deprecated
+ @Getter
+ @Setter
+ private Integer userId;
+
+ public Userdata () {
+ this(-1);
+ }
+
+ // EM: replace with username
+ @Deprecated
+ public Userdata(Integer userid) {
+ this.userId = userid;
+ this.id = -1;
+ this.data = dataFactory.convertData(null);
+ }
+
+ public Userdata (String data) {
+ this.data = dataFactory.convertData(data);
+ }
+
+ public int size () {
+ return dataFactory.size(this.data);
+ }
+
+
+ public Object get (String key) {
+ return dataFactory.getValue(this.data, key);
+ }
+
+ public Object filter(String ... keys) {
+ return dataFactory.filter(this.data, keys);
+ }
+
+
+ /**
+ *
+ * @return
+ */
+ public boolean isValid () {
+ return findMissingFields().length == 0;
+ }
+
+
+ public String[] findMissingFields () {
+ Set<String> missing = new HashSet<>();
+ Set<String> keys = dataFactory.keys(this.data);
+ for (String key : requiredFields()) {
+ if (!keys.contains(key))
+ missing.add(key);
+ }
+ return missing.toArray(new String[0]);
+ }
+
+
+ public void checkRequired () throws KustvaktException {
+ String[] fields = findMissingFields();
+ if (findMissingFields().length != 0) {
+ throw new KustvaktException(userId, StatusCodes.MISSING_PARAMETER,
+ "User data object not valid. Object has missing fields!",
+ Arrays.asList(fields).toString());
+ }
+ }
+
+
+ //fixme: if data array, return empty?!
+ public Set<String> keys () {
+ return dataFactory.keys(this.data);
+ }
+
+
+ public Collection<Object> values () {
+ return dataFactory.values(this.data);
+ }
+
+
+ public void setData (String data) {
+ this.data = dataFactory.convertData(data);
+ }
+
+
+ public void update (Userdata other) {
+ if (other != null && this.getClass().equals(other.getClass()))
+ this.data = dataFactory.merge(this.data, other.data);
+ }
+
+
+ public String serialize () throws KustvaktException {
+ // to have consistency with required fields --> updates/deletion may cause required fields to be missing.
+ this.checkRequired();
+ return dataFactory.toStringValue(this.data);
+ }
+
+
+ public void setField (String key, Object value) {
+ dataFactory.addValue(this.data, key, value);
+ }
+
+ // EM: de.ids_mannheim.korap.interfaces.defaults.ApacheValidator.validateMap(Map<String, Object>)
+ // is not reliable
+ // todo: test
+ public void validate (Validator validator) throws KustvaktException {
+ dataFactory.validate(this.data, validator);
+ }
+
+
+ public void read (Map<String, Object> map, boolean defaults_only)
+ throws KustvaktException {
+ this.readQuietly(map, defaults_only);
+ this.checkRequired();
+ }
+
+
+ public void readQuietly (Map<String, Object> map, boolean defaults_only) {
+ if (map != null){
+ if (defaults_only) {
+ for (String k : defaultFields()) {
+ Object o = map.get(k);
+ if (o != null) {
+ dataFactory.addValue(this.data, k, o);
+ }
+ }
+ }
+ else {
+ for (String key : map.keySet())
+ dataFactory.addValue(this.data, key, map.get(key));
+ }
+ }
+ }
+
+ // EM: added
+ public boolean removeField (String field) {
+ return dataFactory.removeValue(this.data, field);
+ }
+
+ public abstract String[] requiredFields ();
+
+
+ public abstract String[] defaultFields ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java
new file mode 100644
index 0000000..8dbeb4f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/BooleanUtils.java
@@ -0,0 +1,25 @@
+package de.ids_mannheim.korap.utils;
+
+/**
+ * @author hanl
+ * @date 19/02/2014
+ */
+public class BooleanUtils {
+
+ public static String dbname;
+
+
+ public static Object getBoolean (Object val) {
+ if (val == null)
+ val = false;
+ if (dbname != null && dbname.equalsIgnoreCase("sqlite")) {
+ if (val instanceof Boolean) {
+ return ((boolean) val) ? 1 : 0;
+ }
+ else if (val instanceof Integer) {
+ return ((Integer) val == 1);
+ }
+ }
+ return val;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java b/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java
new file mode 100644
index 0000000..6cb0dcd
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/CollectionTypes.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 04/12/2013
+ */
+public class CollectionTypes {
+
+ private ObjectMapper mapper;
+
+
+ public CollectionTypes () {
+ this.mapper = new ObjectMapper();
+ }
+
+
+ public Map createGroup (String relation, String field, List terms) {
+ if (relation == null)
+ return null;
+
+ Map kgroup = new LinkedHashMap<>();
+ kgroup.put("@type", "korap:group");
+ if (field != null)
+ kgroup.put("@field", "korap:field#" + field);
+ kgroup.put("relation", relation);
+ kgroup.put("operands", terms);
+ return kgroup;
+ }
+
+
+ public Map createTerm (String field, String subtype, String value,
+ String type) {
+ Map term = new LinkedHashMap<>();
+ if (type == null)
+ type = "korap:term";
+ term.put("@type", type);
+ if (field != null)
+ term.put("@field", "korap:field#" + field);
+ if (subtype != null)
+ term.put("@subtype", "korap:value#" + subtype);
+ term.put("@value", value);
+ return term;
+ }
+
+
+ public Map createTerm (String field, String value, String type) {
+ return createTerm(field, null, value, type);
+ }
+
+
+ public Map createTerm (String field, String value) {
+ return createTerm(field, value, null);
+ }
+
+
+ public Map createResourceFilter (String resource, Map value) {
+ Map meta = new LinkedHashMap();
+ meta.put("@type", "korap:meta-filter");
+ meta.put("@id", "korap-filter#" + resource);
+ meta.put("@value", value);
+ return meta;
+ }
+
+
+ public Map createResourceFilter (String resource, String value)
+ throws IOException {
+ return createResourceFilter(resource, mapify(value));
+ }
+
+
+ public Map createResourceExtend (String resource, Map value) {
+ Map meta = new LinkedHashMap();
+ meta.put("@type", "korap:meta-extend");
+ meta.put("@id", "korap-filter#" + resource);
+ meta.put("@value", value);
+ return meta;
+ }
+
+
+ public Map createMetaFilter (Map value) {
+ Map meta = new LinkedHashMap();
+ meta.put("@type", "korap:meta-filter");
+ meta.put("@value", value);
+ return meta;
+ }
+
+
+ public Map createMetaExtend (Map value) {
+ Map meta = new LinkedHashMap();
+ meta.put("@type", "korap:meta-extend");
+ meta.put("@value", value);
+ return meta;
+ }
+
+
+ public Map mapify (String s) throws IOException {
+ return mapper.readValue(s, Map.class);
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java b/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java
new file mode 100644
index 0000000..ea02e44
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ConcurrentMultiMap.java
@@ -0,0 +1,148 @@
+package de.ids_mannheim.korap.utils;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.MapMaker;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+
+import com.google.common.collect.Sets;
+
+import java.util.Collection;
+import java.util.Set;
+
+
+/**
+ * A general purpose Multimap implementation for delayed processing and concurrent insertion/deletes.
+ * This code is based on an implementation by Guido Medina!
+ *
+ * @param <K> A comparable Key
+ * @param <V> A comparable Value
+ */
+
+/**
+ * User: hanl
+ * Date: 8/27/13
+ * Time: 11:18 AM
+ */
+
+public class ConcurrentMultiMap<K extends Comparable, V extends Comparable> {
+
+ private final int initialCapacity;
+ private final LockMap<K> locks;
+ private final ConcurrentMap<K, List<V>> cache;
+
+
+ public ConcurrentMultiMap () {
+ this(16, 64);
+ }
+
+
+ public ConcurrentMultiMap (final int concurrencyLevel) {
+ this(concurrencyLevel, 64);
+ }
+
+
+ public ConcurrentMultiMap (final int concurrencyLevel,
+ final int initialCapacity) {
+ this.initialCapacity = initialCapacity;
+ cache = new MapMaker().concurrencyLevel(concurrencyLevel)
+ .initialCapacity(initialCapacity).makeMap();
+ locks = new LockMap<K>(concurrencyLevel, initialCapacity);
+ }
+
+
+ public void put (final K key, final V value) {
+ synchronized (locks.getLock(key)) {
+ List<V> set = cache.get(key);
+ if (set == null) {
+ set = Lists.newArrayListWithExpectedSize(initialCapacity);
+ cache.put(key, set);
+ }
+ set.add(value);
+ }
+ }
+
+
+ public void putAll (final K key, final Collection<V> values) {
+ synchronized (locks.getLock(key)) {
+ List<V> set = cache.get(key);
+ if (set == null) {
+ set = Lists.newArrayListWithExpectedSize(initialCapacity);
+ cache.put(key, set);
+ }
+ set.addAll(values);
+ }
+ }
+
+
+ public List<V> remove (final K key) {
+ synchronized (locks.getLock(key)) {
+ return cache.remove(key);
+ }
+ }
+
+
+ public void remove (final K key, final V value) {
+ List<V> values = cache.get(key);
+ synchronized (locks.getLock(key)) {
+ values.remove(value);
+ }
+ }
+
+
+ public Set<K> getKeySet () {
+ return cache.keySet();
+ }
+
+
+ public int size () {
+ return cache.size();
+ }
+
+
+ public boolean containsKey (K key) {
+ return cache.containsKey(key);
+ }
+
+
+ public List<V> get (K key) {
+ return cache.get(key);
+ }
+
+
+ public class LockMap<K extends Comparable> {
+ private final ConcurrentMap<K, Object> locks;
+
+
+ public LockMap () {
+ this(16, 64);
+ }
+
+
+ public LockMap (final int concurrencyLevel) {
+ this(concurrencyLevel, 64);
+ }
+
+
+ public LockMap (final int concurrencyLevel, final int initialCapacity) {
+ locks = new MapMaker().concurrencyLevel(concurrencyLevel)
+ .initialCapacity(initialCapacity).weakValues().makeMap();
+ }
+
+
+ public Object getLock (final K key) {
+ final Object object = new Object();
+ Object lock = locks.putIfAbsent(key, object);
+ return lock == null ? object : lock;
+ }
+
+ }
+
+
+ public String toString () {
+ return cache.toString();
+ }
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java b/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java
new file mode 100644
index 0000000..c2511a7
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/IPNetMask.java
@@ -0,0 +1,127 @@
+package de.ids_mannheim.korap.utils;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * User: hanl
+ * Date: 9/13/13
+ * Time: 2:25 PM
+ *
+ * currently only supports IPv4!
+ *
+ */
+// todo: integrate to gerrit
+public class IPNetMask {
+
+ private final Inet4Address i4addr;
+ private final byte maskCtr;
+
+ private final int addrInt;
+ private final int maskInt;
+
+ private static final int default_mask = 16;
+
+
+ private IPNetMask (Inet4Address i4addr, byte mask) {
+ this.i4addr = i4addr;
+ this.maskCtr = mask;
+
+ this.addrInt = addrToInt(i4addr);
+ this.maskInt = ~((1 << (32 - maskCtr)) - 1);
+ }
+
+
+ /**
+ * IPNetMask factory method.
+ *
+ * @param addrSlashMask
+ * IP/Mask String in format "nnn.nnn.nnn.nnn/mask". If
+ * the "/mask" is omitted, "/32" (just the single
+ * address) is assumed.
+ * @return a new IPNetMask
+ * @throws UnknownHostException
+ * if address part cannot be parsed by
+ * InetAddress
+ */
+ public static IPNetMask getIPMask (String addrSlashMask)
+ throws UnknownHostException {
+ int pos = addrSlashMask.indexOf('/');
+ String addr;
+ byte maskCtr;
+ if (pos == -1) {
+ addr = addrSlashMask;
+ maskCtr = default_mask;
+ }
+ else {
+ addr = addrSlashMask.substring(0, pos);
+ maskCtr = Byte.parseByte(addrSlashMask.substring(pos + 1));
+ }
+
+ return new IPNetMask((Inet4Address) InetAddress.getByName(addr),
+ maskCtr);
+ }
+
+
+ /**
+ * Test given IPv4 address against this IPNetMask object.
+ *
+ * @param testAddr
+ * address to isSystem.
+ * @return true if address is in the IP Mask range, false if not.
+ */
+ public boolean matches (Inet4Address testAddr) {
+ int testAddrInt = addrToInt(testAddr);
+ return ((addrInt & maskInt) == (testAddrInt & maskInt));
+ }
+
+
+ /**
+ * Convenience method that converts String host to IPv4 address.
+ *
+ * @param addr
+ * IP address to match in nnn.nnn.nnn.nnn format or
+ * hostname.
+ * @return true if address is in the IP Mask range, false if not.
+ * @throws UnknownHostException
+ * if the string cannot be decoded.
+ */
+ public boolean matches (String addr) throws UnknownHostException {
+ return matches((Inet4Address) InetAddress.getByName(addr));
+ }
+
+
+ /**
+ * Converts IPv4 address to integer representation.
+ */
+ private int addrToInt (Inet4Address i4addr) {
+ byte[] ba = i4addr.getAddress();
+ return (ba[0] << 24) | ((ba[1] & 0xFF) << 16) | ((ba[2] & 0xFF) << 8)
+ | (ba[3] & 0xFF);
+ }
+
+
+ @Override
+ public String toString () {
+ return i4addr.getHostAddress() + "/" + maskCtr;
+ }
+
+
+ @Override
+ public boolean equals (Object obj) {
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ final IPNetMask that = (IPNetMask) obj;
+ return (this.addrInt == that.addrInt && this.maskInt == that.maskInt);
+ }
+
+
+ @Override
+ public int hashCode () {
+ return this.maskInt + this.addrInt;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java
new file mode 100644
index 0000000..0a8623f
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/JerseyUtils.java
@@ -0,0 +1,38 @@
+package de.ids_mannheim.korap.utils;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.MediaType;
+
+import org.glassfish.jersey.message.internal.MediaTypes;
+import org.glassfish.jersey.server.ContainerRequest;
+
+public class JerseyUtils {
+
+ /**
+ * Get the form parameters of the request entity.
+ * <p>
+ * This method will ensure that the request entity is buffered
+ * such that it may be consumed by the application.
+ *
+ * @return the form parameters, if there is a request entity and the
+ * content type is "application/x-www-form-urlencoded", otherwise an
+ * instance containing no parameters will be returned.
+ */
+ public static Form getFormParameters (ContainerRequestContext requestContext) {
+ if (requestContext instanceof ContainerRequest) {
+ return getFormParameters((ContainerRequest) requestContext);
+ }
+ return new Form();
+ }
+
+ private static Form getFormParameters (ContainerRequest request) {
+ if (MediaTypes.typeEqual(MediaType.APPLICATION_FORM_URLENCODED_TYPE, request.getMediaType())) {
+ request.bufferEntity();
+ Form form = request.readEntity(Form.class);
+ return (form == null ? new Form() : form);
+ } else {
+ return new Form();
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java
new file mode 100644
index 0000000..792a6bb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/JsonUtils.java
@@ -0,0 +1,123 @@
+package de.ids_mannheim.korap.utils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+/**
+ * @author hanl
+ * @date 28/01/2014
+ */
+public class JsonUtils {
+ private static ObjectMapper mapper = new ObjectMapper();
+
+ private JsonUtils () {}
+
+
+ public static String toJSON (Object values) throws KustvaktException {
+ try {
+ return mapper.writeValueAsString(values);
+ }
+ catch (JsonProcessingException e) {
+ throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+ "Failed serializing object in json", e);
+ }
+ }
+
+
+ public static JsonNode readTree (String json) throws KustvaktException {
+ try {
+ return mapper.readTree(json);
+ }
+ catch (IOException e) {
+ throw new KustvaktException(StatusCodes.DESERIALIZATION_FAILED,
+ "Failed deserializing json object: " + json, json, e);
+ }
+ }
+
+ public static ObjectNode createObjectNode () {
+ return mapper.createObjectNode();
+ }
+
+
+ public static ArrayNode createArrayNode () {
+ return mapper.createArrayNode();
+ }
+
+
+ public static JsonNode valueToTree (Object value) {
+ return mapper.valueToTree(value);
+ }
+
+ public static <T> T convert (JsonNode json, Class<T> cl)
+ throws IOException {
+ return mapper.convertValue(json, cl);
+ }
+
+ public static <T> T read (String json, Class<T> cl) throws IOException {
+ return mapper.readValue(json, cl);
+ }
+
+ public static <T> T read (InputStream is, Class<T> cl) throws IOException {
+ return mapper.readValue(is, cl);
+ }
+
+
+ public static <T> T readFile (String path, Class<T> clazz)
+ throws IOException {
+ return mapper.readValue(new File(path), clazz);
+ }
+
+
+ public static void writeFile (String path, Object content)
+ throws IOException {
+ mapper.writeValue(new File(path), content);
+ }
+
+
+ public static <T> T convertToClass (String json, Class<T> cl) throws KustvaktException {
+ T t = null;
+ try {
+ t = mapper.readValue(json, cl);
+ }
+ catch (IOException e) {
+ throw new KustvaktException(StatusCodes.DESERIALIZATION_FAILED,
+ e.getMessage(), json, e);
+ }
+ return t;
+ }
+
+
+ public static List<Map<String, Object>> convertToList (String json)
+ throws JsonProcessingException, KustvaktException {
+ List d = new ArrayList();
+ JsonNode node = JsonUtils.readTree(json);
+ if (node.isArray()) {
+ Iterator<JsonNode> nodes = node.iterator();
+ while (nodes.hasNext()) {
+ Map<String, Object> map =
+ mapper.treeToValue(nodes.next(), Map.class);
+ d.add(map);
+ }
+ }
+ else if (node.isObject()) {
+ Map<String, Object> map = mapper.treeToValue(node, Map.class);
+ d.add(map);
+ }
+ return d;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java b/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
new file mode 100644
index 0000000..a6260cb
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/KoralCollectionQueryBuilder.java
@@ -0,0 +1,232 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.query.serialize.CollectionQueryProcessor;
+import de.ids_mannheim.korap.response.Notifications;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/**
+ * convenience builder class for collection query
+ *
+ * @author hanl, margaretha
+ * @date 29/06/2017
+ */
+public class KoralCollectionQueryBuilder {
+
+ public enum EQ {
+ EQUAL, UNEQUAL
+ }
+
+ private boolean verbose;
+ private JsonNode base;
+ private StringBuilder builder;
+ private String mergeOperator;
+
+ public KoralCollectionQueryBuilder () {
+ this(false);
+ }
+
+
+ public KoralCollectionQueryBuilder (boolean verbose) {
+ this.verbose = verbose;
+ this.builder = new StringBuilder();
+ this.base = null;
+ this.mergeOperator = null;
+ }
+
+
+ /**
+ * raw method for field - value pair adding. Supports all
+ * operators (leq, geq, contains, etc.)
+ *
+ * @param field
+ * @param op
+ * @param value
+ * @return
+ */
+ public KoralCollectionQueryBuilder with (String field, String op,
+ String value) {
+ //String end = this.builder.substring(this.builder.length() - 4,
+ // this.builder.length() - 1);
+ //if (this.builder.length() != 0
+ // && (!end.contains("&") | !end.contains("|")))
+ // throw new RuntimeException("no join operator given!");
+ this.with(field + op + value);
+ return this;
+ }
+
+
+ /**
+ * element can be a more complex sub query like
+ * (textClass=freizeit & Attributes.CORPUS_SIGLE=WPD)
+ *
+ * @param query
+ * will be enclosed by parenthesis in order to make sub
+ * query
+ * element
+ * @return
+ */
+ public KoralCollectionQueryBuilder with (String query) {
+ if (!query.startsWith("(") && !query.endsWith(")"))
+ query = "(" + query + ")";
+ this.builder.append(query);
+ return this;
+ }
+
+
+
+ public KoralCollectionQueryBuilder and () {
+ if (this.builder.length() != 0) this.builder.append(" & ");
+ if (this.base != null && this.mergeOperator == null)
+ this.mergeOperator = "AND";
+ return this;
+ }
+
+
+ public KoralCollectionQueryBuilder or () {
+ if (this.builder.length() != 0) this.builder.append(" | ");
+ if (this.base != null && this.mergeOperator == null)
+ this.mergeOperator = "OR";
+ return this;
+ }
+
+
+ public JsonNode rebaseCollection () throws KustvaktException {
+ if (this.builder.length() == 0 && this.base == null) return null;
+
+ JsonNode request = null;
+ if (this.builder.length() != 0) {
+ CollectionQueryProcessor tree =
+ new CollectionQueryProcessor(this.verbose);
+ tree.process(this.builder.toString());
+ if (tree.getErrors().size() > 0) {
+ // legacy support
+ for (List<Object> e : tree.getErrors()) {
+ if (e.get(1) instanceof String[]) {
+ Notifications notif = new Notifications();
+ int code = (int) e.get(0);
+ notif.addError(code, (String[]) e.get(1));
+ String notificationStr = notif.toJsonString();
+ throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+ notificationStr, true);
+ }
+ else{
+ break;
+ }
+ }
+ // -- end of legacy support
+
+ Map<String, Object> map = new HashMap<>();
+ map.put("errors", tree.getErrors());
+ String errors = JsonUtils.toJSON(map);
+ throw new KustvaktException(StatusCodes.SERIALIZATION_FAILED,
+ errors, true);
+ }
+ request = JsonUtils.valueToTree(tree.getRequestMap());
+ }
+
+ if (this.base != null) {
+ // check that collection non empty
+ JsonNode tmp = this.base.deepCopy();
+ if (request != null)
+ request = mergeWith(request);
+ else
+ request = tmp;
+ }
+ return request;
+ }
+
+
+ public JsonNode mergeWith (JsonNode node) {
+ if (this.base != null) {
+ if (node != null) {
+ JsonNode tobase = node.at("/collection");
+ JsonNode base = this.base.deepCopy();
+ JsonNode result = base.at("/collection");
+
+ if (result.isMissingNode() && !tobase.isMissingNode())
+ result = tobase;
+ else if (result.isMissingNode() && tobase.isMissingNode())
+ return base;
+ else {
+ result = JsonBuilder.buildDocGroup(
+ this.mergeOperator != null
+ ? this.mergeOperator.toLowerCase() : "and",
+ result, tobase);
+ }
+ ((ObjectNode) base).put("collection", result);
+ return base;
+ }
+ return this.base;
+ }
+ throw new RuntimeException("no query found to merge with!");
+ }
+
+
+ /**
+ * sets base query. All consequent queries are added to the first
+ * koral:docGroup within the collection base query
+ * If no group in base query, consequent queries are skipped.
+ *
+ * @param query
+ * @throws KustvaktException
+ */
+ public KoralCollectionQueryBuilder setBaseQuery (String query) throws KustvaktException {
+ this.base = JsonUtils.readTree(query);
+ return this;
+ }
+
+
+ public KoralCollectionQueryBuilder setBaseQuery (JsonNode query) {
+ this.base = query;
+ return this;
+ }
+
+
+ public String toJSON () throws KustvaktException {
+ return JsonUtils.toJSON(rebaseCollection());
+ }
+
+
+ @Override
+ public String toString () {
+ return this.builder.toString();
+ }
+
+
+ private static class JsonBuilder {
+
+ public static ObjectNode buildDoc (String key, String value) {
+ ObjectNode node = JsonUtils.createObjectNode();
+ node.put("@type", "koral:doc");
+ // eq.equals(EQ.EQUAL) ? "match:eq" : "match:ne"
+ node.put("match", "match:eq");
+ node.put("key", key);
+ node.put("value", value);
+ return node;
+ }
+
+
+ public static ObjectNode buildDocGroup (String op,
+ JsonNode ... groups) {
+ ObjectNode node = JsonUtils.createObjectNode();
+ node.put("@type", "koral:docGroup");
+ node.put("operation", "operation:" + op);
+ ArrayNode ops = JsonUtils.createArrayNode();
+ ops.addAll(Arrays.asList(groups));
+ node.put("operands", ops);
+ return node;
+ }
+
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java
new file mode 100644
index 0000000..beafb32
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/NamingUtils.java
@@ -0,0 +1,19 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Created by hanl on 14.05.16.
+ */
+public class NamingUtils {
+
+
+
+ private NamingUtils () {}
+
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java b/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java
new file mode 100644
index 0000000..2673c80
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ParameterChecker.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.Collection;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+public class ParameterChecker {
+
+ public static void checkObjectValue (Object obj, String name)
+ throws KustvaktException {
+ if (obj == null) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is null", name);
+ }
+ }
+
+ public static void checkCollection (Collection<?> collection, String name)
+ throws KustvaktException {
+ if (collection == null) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is null", name);
+ }
+ else if (collection.isEmpty()) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is empty", name);
+ }
+ }
+
+ public static void checkStringValue (String string, String name)
+ throws KustvaktException {
+ if (string == null) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is null", name);
+ }
+ else if (string.isEmpty()) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is empty", name);
+ }
+ }
+
+ public static void checkIntegerValue (int integer, String name)
+ throws KustvaktException {
+ if (integer == 0) {
+ throw new KustvaktException(StatusCodes.MISSING_PARAMETER,
+ name + " is missing", name);
+ }
+ }
+
+ public static void checkNameValue (String value, String name)
+ throws KustvaktException {
+ if (value == null) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name + " is null", name);
+ }
+ else if (value.length() < 3) {
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ name+" must contain at least 3 characters", name);
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java b/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java
new file mode 100644
index 0000000..20e94d5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/PrefixTreeMap.java
@@ -0,0 +1,48 @@
+package de.ids_mannheim.korap.utils;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * @author hanl
+ * @date 01/07/2014
+ */
+public class PrefixTreeMap<V> extends TreeMap<String, V> {
+
+
+ public SortedMap<String, V> getPrefixSubMap (String prefix) {
+ if (prefix != null && prefix.length() > 0) {
+ SortedMap d = this.subMap(prefix, getEnd(prefix));
+ if (d.isEmpty())
+ return null;
+ return d;
+ }
+ return null;
+ }
+
+
+ private String getEnd (String prefix) {
+ char nextLetter = (char) (prefix.charAt(prefix.length() - 1) + 1);
+ return prefix.substring(0, prefix.length() - 1) + nextLetter;
+
+ }
+
+
+ public V getFirstValue (String prefix) {
+ if (prefix.length() > 0) {
+ String first = this.subMap(prefix, getEnd(prefix)).firstKey();
+ return this.get(first);
+ }
+ return null;
+ }
+
+
+ public V getLastValue (String prefix) {
+ if (prefix.length() > 0) {
+ String last = this.subMap(prefix, getEnd(prefix)).lastKey();
+ return this.get(last);
+ }
+ return null;
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java b/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java
new file mode 100644
index 0000000..0b16cc0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/PropertyReader.java
@@ -0,0 +1,37 @@
+package de.ids_mannheim.korap.utils;
+
+import de.ids_mannheim.korap.config.BeanInjectable;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author hanl
+ * @date 27/09/2014
+ */
+public abstract class PropertyReader {
+
+ protected Map<String, Properties> read (String path) throws IOException {
+ Map<String, Properties> res = new HashMap<>();
+ Properties s = new Properties();
+ s.load(new FileInputStream(new File(path)));
+ for (Map.Entry<Object, Object> e : s.entrySet()) {
+ String key = e.getKey().toString().split("\\.")[0];
+ Properties in = res.get(key);
+ if (in == null) {
+ in = new Properties();
+ res.put(key, in);
+ }
+ in.setProperty(e.getKey().toString(), e.getValue().toString());
+ }
+ return res;
+ }
+
+
+ public abstract void load ();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java b/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
new file mode 100644
index 0000000..fa83ebc
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/ServiceInfo.java
@@ -0,0 +1,91 @@
+package de.ids_mannheim.korap.utils;
+
+import de.ids_mannheim.korap.config.ConfigLoader;
+import de.ids_mannheim.korap.query.serialize.QuerySerializer;
+import lombok.Getter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+/**
+ * @author hanl
+ * @date 23/01/2014
+ */
+public class ServiceInfo {
+
+ private static final ServiceInfo info = new ServiceInfo();
+
+ public static final String UNKNOWN = "UNKNOWN";
+
+ @Getter
+ private String name;
+ @Getter
+ private String version;
+ @Getter
+ private String config;
+ @Getter
+ private String logger;
+ @Getter
+ private Boolean cacheable;
+ @Getter
+ private String cache_store;
+
+
+ @Getter
+ private String krillVersion;
+ @Getter
+ private String koralVersion;
+
+ private ServiceInfo () {
+ load();
+ }
+
+
+ private void load () {
+ Properties props = new Properties();
+ try {
+
+ InputStream stream = getStream();
+ props.load(stream);
+ stream.close();
+ this.version = (String) props.get("kustvakt.version");
+ this.name = (String) props.get("kustvakt.name");
+ this.config = (String) props.get("kustvakt.properties");
+ this.logger = (String) props.get("kustvakt.logging");
+ this.cacheable = Boolean.valueOf((String) props.get("kustvakt.cache"));
+ this.cache_store = (String) props.get("kustvakt.cache_store");
+
+ this.krillVersion=(String) props.get("krill.version");
+
+ QuerySerializer s = new QuerySerializer();
+ this.koralVersion = s.getVersion() ;
+ }
+ catch (IOException e) {
+ this.version = UNKNOWN;
+ this.name = UNKNOWN;
+ this.logger = UNKNOWN;
+ this.config = UNKNOWN;
+ this.cacheable = false;
+ this.cache_store = UNKNOWN;
+
+ this.koralVersion = UNKNOWN;
+ this.krillVersion = UNKNOWN;
+ }
+ }
+
+
+ private static InputStream getStream () throws IOException {
+ String path = "service.properties";
+ InputStream stream = ConfigLoader.loadConfigStream(path);
+ if (stream == null)
+ throw new IOException("stream for resource " + path
+ + " could not be found...");
+ return stream;
+ }
+
+
+ public static ServiceInfo getInfo () {
+ return info;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java b/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java
new file mode 100644
index 0000000..fa1314b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/SqlBuilder.java
@@ -0,0 +1,103 @@
+package de.ids_mannheim.korap.utils;
+
+/**
+ * @author hanl
+ * @date 26/11/2015
+ */
+public class SqlBuilder {
+
+ private StringBuffer buffer;
+ private String table;
+ private String[] fields;
+ private String where;
+
+
+ public SqlBuilder (String table) {
+ this.buffer = new StringBuffer();
+ this.table = table;
+ }
+
+
+ public SqlBuilder select (String ... fields) {
+ this.buffer.append("SELECT ");
+ if (fields.length > 0) {
+ for (int i = 0; i < fields.length; i++) {
+ if (i > 0)
+ this.buffer.append(", ");
+ this.buffer.append(fields[i]);
+ }
+ }
+ else
+ this.buffer.append("*");
+ this.buffer.append(" FROM ").append(table);
+ return this;
+ }
+
+
+ public SqlBuilder update (String ... fields) {
+ this.buffer.append("UPDATE ").append(table);
+ this.fields = fields;
+ return this;
+ }
+
+
+ public SqlBuilder insert (String ... fields) {
+ this.buffer.append("INSERT INTO ").append(table);
+ this.fields = fields;
+ return this;
+ }
+
+
+ public SqlBuilder delete () {
+ this.buffer.append("DELETE FROM ").append(table);
+ return this;
+ }
+
+
+ public SqlBuilder params (String ... values) {
+ if (values.length != fields.length)
+ return this;
+ if (this.buffer.lastIndexOf("INSERT INTO") != -1) {
+ this.buffer.append(" (");
+ for (int i = 0; i < this.fields.length; i++) {
+ if (i > 0)
+ this.buffer.append(", ");
+ this.buffer.append(fields[i]);
+ }
+
+ StringBuffer b = new StringBuffer();
+ for (int i = 0; i < values.length; i++) {
+ if (i > 0)
+ b.append(", ");
+ b.append(values[i]);
+ }
+ this.buffer.append(") VALUES (").append(b.toString()).append(")");
+ }
+ if (this.buffer.lastIndexOf("UPDATE") != -1) {
+ this.buffer.append(" SET ");
+ for (int i = 0; i < this.fields.length; i++) {
+ if (i > 0)
+ this.buffer.append(", ");
+ this.buffer.append(fields[i]).append("=").append(values[i]);
+ }
+ }
+ return this;
+ }
+
+
+ public SqlBuilder where (String where) {
+ this.where = where;
+ return this;
+ }
+
+
+ @Override
+ public String toString () {
+ StringBuffer b = new StringBuffer(this.buffer);
+ //exclude where clauses from insert statements
+ if (this.where != null && this.buffer.lastIndexOf("INSERT INTO") == -1)
+ b.append(" WHERE ").append(where);
+ return b.append(";").toString();
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
new file mode 100644
index 0000000..99c9578
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/StringUtils.java
@@ -0,0 +1,226 @@
+package de.ids_mannheim.korap.utils;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+
+public class StringUtils {
+ private final static Logger jlog = LogManager
+ .getLogger(StringUtils.class);
+
+ private static final String SEP = ";";
+ private static final String SLASH = "/";
+
+
+ public static Collection<UUID> stringToUUIDList (String s) {
+ String[] array = s.split(SEP);
+ List<UUID> list = new LinkedList<>();
+ for (String att : array) {
+ list.add(UUID.fromString(att));
+ }
+ return list;
+ }
+
+
+ public static List<String> toList (String values) {
+ List<String> list = new ArrayList<>();
+ StringTokenizer tokenizer = new StringTokenizer(values, SEP);
+ while (tokenizer.hasMoreTokens())
+ list.add(tokenizer.nextToken());
+ return list;
+ }
+
+
+ public static Set<String> toSet (String values, String sep) {
+ Set<String> set = new HashSet<>();
+ if (values != null && !values.isEmpty()) {
+ StringTokenizer tokenizer = new StringTokenizer(values, sep);
+ while (tokenizer.hasMoreTokens())
+ set.add(tokenizer.nextToken());
+ }
+ return set;
+ }
+
+
+ public static Set<String> toSet (String values) {
+ return toSet(values, SEP);
+ }
+
+
+ public static String toString (Collection<String> values) {
+ return StringUtils.toString(values, SEP);
+ }
+
+
+ public static String toString (Collection<String> values, String sep) {
+ StringBuffer b = new StringBuffer();
+ for (String s : values)
+ b.append(s).append(sep);
+
+ if (b.length() > 0)
+ b.deleteCharAt(b.length() - 1);
+ return b.toString();
+ }
+
+
+ public static String orderedToString (Collection<String> hash) {
+ Set<String> orderedSet = new TreeSet<>();
+ orderedSet.addAll(hash);
+ if (orderedSet.isEmpty()) {
+ return "";
+ }
+ else {
+ StringBuilder builder = new StringBuilder();
+ for (String s : orderedSet) {
+ builder.append(s);
+ builder.append(SEP);
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+ }
+
+
+ public static String UUIDsetToString (Collection<UUID> hash) {
+ Set<UUID> orderedSet = new TreeSet<>();
+ orderedSet.addAll(hash);
+ if (orderedSet.isEmpty()) {
+ return "";
+ }
+ else {
+ StringBuilder builder = new StringBuilder();
+ for (UUID s : orderedSet) {
+ builder.append(s);
+ builder.append(SEP);
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+ }
+
+
+ public static String buildSQLRegex (String path) {
+ StringBuilder b = new StringBuilder();
+ String[] match = path.split("/");
+ b.append(match[0]);
+ b.append("(" + "/" + match[1] + ")");
+ b.append("*$");
+ return b.toString();
+ }
+
+
+ // todo: move to parameter utils
+ public static boolean isInteger (String value) {
+ try {
+ Integer.valueOf(value);
+ return true;
+ }
+ catch (IllegalArgumentException e) {
+ // do nothing!
+ return false;
+ }
+ }
+
+
+ public static String normalize (String value) {
+ return value.trim().toLowerCase();
+ }
+
+
+ public static String normalizeHTML (String value) {
+ return StringEscapeUtils.escapeHtml(value);
+ }
+
+
+ public static String decodeHTML (String value) {
+ return StringEscapeUtils.unescapeHtml(value);
+ }
+
+
+ public static String getDocSigle (String textSigle) {
+ if (textSigle != null)
+ return textSigle.split("\\.")[0];
+ return null;
+ }
+
+
+ public static String getCorpusSigle (String textSigle) {
+ //WPD_SSS.07367
+ if (textSigle != null)
+ return textSigle.split("_")[0];
+ return null;
+ }
+
+
+ public static Collection<String> joinStringSet (Collection<String> source,
+ String other) {
+ Set<String> set = new HashSet<>(source);
+ set.add(other);
+ return set;
+ }
+
+
+ public static Collection<UUID> joinUUIDSet (Collection<UUID> source,
+ UUID other) {
+ Set<UUID> set = new HashSet<>(source);
+ set.add(other);
+ return set;
+ }
+
+
+ public static String joinResources (String first, String second) {
+ String res;
+ if (first != null && !first.isEmpty())
+ res = first + SLASH + second;
+ else
+ res = second;
+ return res.replaceAll("\\s", "");
+ }
+
+
+ public static String[] splitAnnotations (String joined) {
+ String[] spl = joined.split(SLASH);
+ if (spl.length == 2)
+ return spl;
+ else
+ return null;
+ }
+
+
+ public static String stripTokenType (String token) {
+ int idx = token.lastIndexOf(" ");
+ if (idx == -1)
+ return token;
+ return token.substring(idx).replaceAll("\\s", "");
+ }
+
+
+ public static String getTokenType (String token) {
+ if (token.contains(" "))
+ return token.substring(0, token.lastIndexOf(" "))
+ .replaceAll("\\s", "").toLowerCase();
+ else
+ return null;
+ }
+
+ public static String toSHAHash(String input) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(input.getBytes());
+ byte[] mdbytes = md.digest();
+
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < mdbytes.length; i++)
+ sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1));
+ return sb.toString();
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java b/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
new file mode 100644
index 0000000..d6f2daf
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/utils/TimeUtils.java
@@ -0,0 +1,227 @@
+package de.ids_mannheim.korap.utils;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import de.ids_mannheim.korap.config.Attributes;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * @author hanl
+ * <p/>
+ * calculates current, expiration and inactive time for
+ * security
+ * purposes.
+ * @return
+ */
+public class TimeUtils {
+
+ private static DecimalFormat df = new DecimalFormat("#.#############");
+ private static DateTimeFormatter dtf = DateTimeFormat
+ .forPattern("dd/MM/yyyy");
+ private static final DateTimeZone dtz = DateTimeZone.forID(Attributes.DEFAULT_TIME_ZONE);
+ private static final boolean DEBUG = false;
+ private static Logger jlog = LogManager.getLogger(TimeUtils.class);
+
+
+ public static int convertTimeToSeconds (String expirationVal) {
+ expirationVal = expirationVal.trim();
+ int finIndex = expirationVal.length() - 1;
+ char entity = expirationVal.charAt(finIndex);
+ int returnSec = Integer.valueOf(expirationVal.substring(0, finIndex));
+ if (DEBUG) {
+ jlog.debug("setting time value to " + returnSec + " with time in "
+ + entity);
+ }
+ switch (entity) {
+ case 'D':
+ return returnSec * 60 * 60 * 24;
+ case 'H':
+ return returnSec * 60 * 60;
+ case 'M':
+ return returnSec * 60;
+ case 'S':
+ return returnSec;
+ default:
+ if (DEBUG){
+ jlog.debug("no time unit specified. Trying to read from default (minutes)");
+ }
+ return Integer.valueOf(expirationVal) * 60;
+ }
+
+ }
+
+ public static DateTime getNow () {
+ return DateTime.now(dtz);
+ }
+
+ public static DateTime getTime(String time) {
+ return DateTime.parse(time).withZone(dtz);
+ }
+
+ public static DateTime getTime(long time) {
+ return new DateTime(time).withZone(dtz);
+ }
+
+ //returns difference in milliseconds
+ public static long calcDiff (DateTime now, DateTime future) {
+ long diff = (future.withZone(dtz).getMillis() - now.withZone(dtz)
+ .getMillis());
+ return diff;
+ }
+
+
+ public static boolean isExpired(long time) {
+ return getNow().isAfter(time);
+
+ }
+
+
+ // returns difference in seconds in floating number
+ public static float floating (DateTime past, DateTime now) {
+ long diff = (now.withZone(dtz).getMillis() - past.withZone(dtz)
+ .getMillis());
+ double fin = diff / 1000.0;
+ BigDecimal bd = new BigDecimal(fin).setScale(8, RoundingMode.HALF_EVEN);
+ return bd.floatValue();
+ }
+
+
+ public static DateTime fromCosmas (String date) {
+ int idx = date.length();
+ try {
+ Integer sec = Integer.valueOf(date.substring((idx = idx - 2),
+ date.length()).trim());
+ Integer min = Integer.valueOf(date.substring((idx = idx - 2),
+ idx + 2).trim());
+ Integer hours = Integer.valueOf(date.substring((idx = idx - 2),
+ idx + 2).trim());
+ Integer day = Integer.valueOf(date.substring((idx = idx - 2),
+ idx + 2).trim());
+ Integer month = Integer.valueOf(date.substring((idx = idx - 2),
+ idx + 2).trim());
+ Integer year = Integer.valueOf(date.substring((idx = idx - 4),
+ idx + 4).trim());
+ return new DateTime(year, month, day, hours, min, sec);
+ }
+ catch (NumberFormatException e) {
+ return getNow().toDateTime();
+ }
+ }
+
+
+ public static String formatDiff (DateTime now, DateTime after) {
+ return df.format(calcDiff(now, after));
+ }
+
+
+ /**
+ * converts time to the ISO8601 standard.
+ *
+ * @param time
+ * @return
+ */
+ public static String format (DateTime time) {
+ DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
+ return fmt.withZone(dtz).print(time);
+ }
+
+
+ public static String format (long time) {
+ DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
+ return fmt.withZone(dtz).print(time);
+ }
+
+
+ /**
+ * calculate expiration time
+ *
+ * @param creation
+ * @param plus
+ * time in seconds
+ * @return
+ */
+ public static DateTime plusSeconds (long creation, int plus) {
+ return new DateTime(creation).withZone(dtz).plusSeconds(plus);
+ }
+
+
+ public static DateTime getExpiration (long now, int exp) {
+ return new DateTime(now).withZone(dtz).plusSeconds(exp);
+ }
+
+
+ /**
+ * @param plus
+ * @return
+ */
+ public static DateTime plusSeconds (int plus) {
+ return getNow().withZone(dtz).plusSeconds(plus);
+ }
+
+
+ public static DateTime plusHours (int hours) {
+ return getNow().withZone(dtz).plusHours(hours);
+ }
+
+
+ public static DateTime plusMinutes (int minutes) {
+ return getNow().withZone(dtz).plusMinutes(minutes);
+ }
+
+
+ /**
+ * create time stamp from long value
+ *
+ * @param t
+ * time
+ * @return Timestamp
+ */
+ public static LocalDate getTimeStamp (long t) {
+ return new DateTime(t).withZone(dtz).toLocalDate();
+ }
+
+
+ public static DateTime getDate (int day, int month, int year) {
+ DateTime date = new DateTime().withZone(dtz);
+ return date.withDate(year, month, day);
+ }
+
+
+ public static String toString (long val, Locale locale) {
+ if (locale == Locale.GERMAN)
+ return new DateTime(val).toString("dd. MMMM yyyy, HH:mm",
+ Locale.GERMAN);
+ else
+ return new DateTime(val).toString("MM-dd-yyyy, hh:mm",
+ Locale.ENGLISH);
+
+ }
+
+
+ public static String dateToString (long val, int i) {
+ switch (i) {
+ case 1:
+ return new DateTime(val).toString("yyyy-MM");
+ case 2:
+ return new DateTime(val).toString("yyyy-MM-dd");
+ default:
+ return new DateTime(val).toString("yyyy");
+ }
+ }
+
+ private static final List<DateTime> times = new ArrayList<>();
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java b/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java
new file mode 100644
index 0000000..c88a0da
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/validator/ApacheValidator.java
@@ -0,0 +1,134 @@
+package de.ids_mannheim.korap.validator;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.validator.routines.DateValidator;
+import org.apache.commons.validator.routines.EmailValidator;
+import org.apache.commons.validator.routines.RegexValidator;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.ConfigLoader;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.web.utils.KustvaktMap;
+
+/**
+ * Created by hanl on 09.06.16.
+ *
+ */
+public class ApacheValidator implements Validator {
+
+ private static Logger jlog = LogManager.getLogger(ApacheValidator.class);
+
+ private static final String STRING_PATTERN = "^[\\.;:,&\\|@\\[\\]\\=\\*\\/\\/_()\\-0-9\\p{L}\\p{Space}]{0,1024}$";
+
+ private static final boolean DEBUG = false;
+
+ private Map<String, RegexValidator> validators;
+
+
+ public ApacheValidator () throws IOException {
+ this.validators = load();
+ }
+
+
+ private static Map<String, RegexValidator> load () throws IOException {
+ Map<String, RegexValidator> validatorMap = new HashMap<>();
+ Properties p = ConfigLoader.loadProperties("validation.properties");
+
+ for (String property : p.stringPropertyNames()) {
+ if (property.startsWith("Validator")) {
+ String name = property.replace("Validator.", "");
+ RegexValidator v = new RegexValidator(
+ p.get(property).toString());
+ validatorMap.put(name, v);
+ }
+ }
+ return validatorMap;
+ }
+
+
+
+ @Override
+ public Map<String, Object> validateMap (Map<String, Object> map)
+ throws KustvaktException {
+ Map<String, Object> safeMap = new HashMap<>();
+ KustvaktMap kmap = new KustvaktMap(map);
+
+ if (map != null) {
+ loop: for (String key : kmap.keySet()) {
+ Object value = kmap.getRaw(key);
+ if (value instanceof List) {
+ List list = (List) value;
+ for (int i = 0; i < list.size(); i++) {
+ if (!isValid(String.valueOf(list.get(i)), key)) {
+ // list.remove(i);
+ throw new KustvaktException(
+ StatusCodes.ILLEGAL_ARGUMENT,
+ "The value for the parameter " + key
+ + " is not valid or acceptable.");
+ }
+ }
+
+ if (list.size() == 1)
+ value = list.get(0);
+ else
+ value = list;
+ }
+ else {
+ if (!isValid(kmap.get(key), key))
+ continue loop;
+ }
+ safeMap.put(key, value);
+ }
+ }
+ return safeMap;
+ }
+
+
+ @Override
+ public String validateEntry (String input, String type)
+ throws KustvaktException {
+ if (!isValid(input, type))
+ throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
+ "Entry did not validate for type '" + type + "'", input);
+ return input;
+ }
+
+
+ @Override
+ public boolean isValid (String input, String type) {
+ boolean valid = false;
+ RegexValidator validator = this.validators.get(type);
+ if (validator != null) {
+ valid = validator.isValid(input);
+ }
+ else {
+ if (Attributes.EMAIL.equals(type)) {
+ valid = EmailValidator.getInstance().isValid(input);
+ }
+ else if ("date".equals(type)) {
+ valid = DateValidator.getInstance().isValid(input);
+ }
+ else if ("string".equals(type)
+ && !this.validators.containsKey("string")) {
+ RegexValidator regex = new RegexValidator(STRING_PATTERN);
+ valid = regex.isValid(input);
+ }
+ else
+ return this.isValid(input, "string");
+ }
+ if (DEBUG){
+ jlog.debug("validating entry "+input+" of type "+type+": "+ (
+ valid ? "Is valid!" : "Is not valid!"));
+ }
+
+ return valid;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java b/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java
new file mode 100644
index 0000000..b20cb3b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/validator/Validator.java
@@ -0,0 +1,26 @@
+package de.ids_mannheim.korap.validator;
+
+import java.util.Map;
+
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+
+/**
+ * EM: made this as a spring component
+ *
+ * Created by hanl on 08.06.16.
+ */
+@Component
+public interface Validator {
+
+
+ Map<String, Object> validateMap (Map<String, Object> map) throws KustvaktException;
+
+
+ String validateEntry (String input, String type)
+ throws KustvaktException;
+
+
+ boolean isValid (String input, String type);
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java
new file mode 100644
index 0000000..4ae0a53
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/ClientsHandler.java
@@ -0,0 +1,59 @@
+package de.ids_mannheim.korap.web;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MultivaluedMap;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 10/12/2013
+ */
+// use for Piotr Ps. rest api connection
+public class ClientsHandler {
+
+ private WebTarget service;
+
+
+ public ClientsHandler (URI address) {
+ Client client = ClientBuilder.newClient();
+ this.service = client.target(address);
+ }
+
+
+ public String getResponse (String path, String key, Object value)
+ throws KustvaktException {
+ try {
+ return service.path(path).queryParam(key, value).request().get(String.class);
+ }
+ catch (WebApplicationException e) {
+ throw new KustvaktException(StatusCodes.REQUEST_INVALID);
+ }
+ }
+
+
+ public String getResponse (MultivaluedMap<String, String> map, String ... paths)
+ throws KustvaktException {
+ try {
+ WebTarget resource = service;
+ for (String p : paths)
+ resource = resource.path(p);
+ for (Map.Entry<String, List<String>> e : map.entrySet()) {
+ for (String value : e.getValue())
+ resource = resource.queryParam(e.getKey(), value);
+ }
+ return resource.request().get(String.class);
+ }
+ catch (WebApplicationException e) {
+ throw new KustvaktException(StatusCodes.REQUEST_INVALID);
+ }
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java
new file mode 100644
index 0000000..a94d0b3
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/CoreResponseHandler.java
@@ -0,0 +1,107 @@
+package de.ids_mannheim.korap.web;
+
+import java.util.List;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import de.ids_mannheim.korap.auditing.AuditRecord;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+import de.ids_mannheim.korap.response.Notifications;
+
+/**
+ * @author hanl, margaretha
+ * @date 29/01/2014
+ * @last 04/12/2017
+ */
+public class CoreResponseHandler {
+
+ private AuditingIface auditing;
+
+ public CoreResponseHandler (AuditingIface iface) {
+ this.auditing = iface;
+ }
+
+ private void register (List<AuditRecord> records) {
+ if (auditing != null && !records.isEmpty())
+ auditing.audit(records);
+ else if (auditing == null)
+ throw new RuntimeException("Auditing handler must be set!");
+ }
+
+
+ public WebApplicationException throwit (KustvaktException e) {
+ Response s;
+ if (e.hasNotification()) {
+ if (e.getStatusCode() != null) {
+ s = Response.status(getStatus(e.getStatusCode()))
+ .entity(e.getNotification()).build();
+ }
+ // KustvaktException just wraps another exception
+ else {
+ s=Response.status(Response.Status.BAD_REQUEST)
+ .entity(e.getNotification()).build();
+ }
+ }
+ else {
+ s = Response.status(getStatus(e.getStatusCode()))
+ .entity(buildNotification(e)).build();
+ }
+ return new WebApplicationException(s);
+ }
+
+ public WebApplicationException throwit (int code) {
+ return new WebApplicationException(Response.status(getStatus(code))
+ .entity(buildNotification(code, "", "")).build());
+ }
+
+
+ public WebApplicationException throwit (int code, String message,
+ String entity) {
+ return new WebApplicationException(Response.status(getStatus(code))
+ .entity(buildNotification(code, message, entity)).build());
+ }
+
+ public WebApplicationException throwit (int code, String notification) {
+ return new WebApplicationException(
+ Response.status(getStatus(code)).entity(notification).build());
+ }
+
+ protected String buildNotification (KustvaktException e) {
+ register(e.getRecords());
+ return buildNotification(e.getStatusCode(), e.getMessage(),
+ e.getEntity());
+ }
+
+ public static String buildNotification (int code, String message,
+ String entity) {
+ Notifications notif = new Notifications();
+ notif.addError(code, message, entity);
+ return notif.toJsonString() + "\n";
+ }
+
+ protected Response.Status getStatus (int code) {
+ Response.Status status = Response.Status.BAD_REQUEST;
+ switch (code) {
+ // case StatusCodes.NO_VALUE_FOUND:
+ // status = Response.Status.NO_CONTENT;
+ // break;
+ case StatusCodes.ILLEGAL_ARGUMENT:
+ status = Response.Status.NOT_ACCEPTABLE;
+ break;
+ case StatusCodes.STATUS_OK:
+ status = Response.Status.OK;
+ break;
+ // EM: Added
+ case StatusCodes.NO_RESOURCE_FOUND:
+ status = Response.Status.NOT_FOUND;
+ break;
+ case StatusCodes.CACHING_VC:
+ status = Response.Status.SERVICE_UNAVAILABLE;
+ break;
+ }
+ return status;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
new file mode 100644
index 0000000..8a7e2f3
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/KustvaktResponseHandler.java
@@ -0,0 +1,71 @@
+package de.ids_mannheim.korap.web;
+
+import java.util.EnumSet;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+import de.ids_mannheim.korap.constant.AuthenticationScheme;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.interfaces.db.AuditingIface;
+
+/**
+ * KustvaktResponseHandler includes exceptions regarding
+ * authorization.
+ *
+ * @author margaretha
+ *
+ */
+public class KustvaktResponseHandler extends CoreResponseHandler {
+
+ public KustvaktResponseHandler (AuditingIface iface) {
+ super(iface);
+ }
+
+ @Override
+ public WebApplicationException throwit (KustvaktException e) {
+ Response r;
+
+ // KustvaktException just wraps another exception
+ if (e.getStatusCode() == null && e.hasNotification()) {
+ r = Response.status(Response.Status.BAD_REQUEST)
+ .entity(e.getNotification()).build();
+ }
+ else if (e.getStatusCode() == StatusCodes.USER_REAUTHENTICATION_REQUIRED
+ || e.getStatusCode() == StatusCodes.AUTHORIZATION_FAILED
+ || e.getStatusCode() >= StatusCodes.AUTHENTICATION_FAILED) {
+ String notification = buildNotification(e.getStatusCode(),
+ e.getMessage(), e.getEntity());
+ r = createUnauthenticatedResponse(notification);
+ }
+ else if (e.hasNotification()) {
+ r = Response.status(getStatus(e.getStatusCode()))
+ .entity(e.getNotification()).build();
+ }
+ else {
+ String notification = buildNotification(e.getStatusCode(),
+ e.getMessage(), e.getEntity());
+ r = Response.status(getStatus(e.getStatusCode()))
+ .entity(notification).build();
+ }
+ return new WebApplicationException(r);
+ }
+
+ public Response createUnauthenticatedResponse (String notification) {
+ ResponseBuilder builder = Response.status(Response.Status.UNAUTHORIZED);
+
+ EnumSet<AuthenticationScheme> schemes = EnumSet
+ .allOf(AuthenticationScheme.class);
+ schemes.remove(AuthenticationScheme.API);
+
+ for (AuthenticationScheme s : schemes) {
+ builder = builder.header(HttpHeaders.WWW_AUTHENTICATE,
+ s.displayName() + " realm=\"Kustvakt\"");
+ }
+
+ return builder.entity(notification).build();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
new file mode 100644
index 0000000..6d6f52b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -0,0 +1,392 @@
+// Connector to the Lucene Backend
+package de.ids_mannheim.korap.web;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.lucene.store.MMapDirectory;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.Krill;
+import de.ids_mannheim.korap.KrillCollection;
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.response.Match;
+import de.ids_mannheim.korap.response.MetaFields;
+import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.util.QueryException;
+
+/**
+ * The SearchKrill class allows for searching in the
+ * Lucene based Krill backend by applying KoralQuery.
+ *
+ * @author Nils Diewald
+ */
+public class SearchKrill {
+ private final static Logger jlog = LogManager.getLogger(SearchKrill.class);
+
+ private static final boolean DEBUG = false;
+
+ public static KrillIndex index;
+
+ /**
+ * Constructor
+ */
+ // todo: use korap.config to get index location
+ public SearchKrill (String path) {
+
+ try {
+ if (path.equals(":temp:")) {
+ index = new KrillIndex();
+ }
+ else {
+ File f = new File(path);
+ jlog.info("Loading index from " + path);
+ if (!f.exists()) {
+ jlog.error("Index not found: " + path + "!");
+ System.exit(-1);
+ }
+ index = new KrillIndex(new MMapDirectory(Paths.get(path)));
+ };
+ }
+ catch (IOException e) {
+ jlog.error("Unable to loadSubTypes index:" + e.getMessage());
+ };
+ };
+
+
+ public KrillIndex getIndex () {
+ return index;
+ };
+
+
+ public void closeIndexReader () throws KustvaktException {
+ try {
+ index.closeReader();
+ }
+ catch (IOException e) {
+ throw new KustvaktException(500, "Failed closing index reader");
+ }
+ }
+
+
+ /**
+ * Search in the Lucene index.
+ *
+ * @param json
+ * JSON-LD string with search and potential meta
+ * filters.
+ */
+ public String search (String json) {
+ if (DEBUG) {
+ jlog.debug(json);
+ }
+ if (index != null) {
+ String result = new Krill(json).apply(index).toJsonString();
+ if (DEBUG) {
+ jlog.debug(result);
+ }
+ return result;
+ }
+ Result kr = new Result();
+ kr.addError(601, "Unable to find index");
+ return kr.toJsonString();
+ };
+
+
+ /**
+ * Search in the Lucene index and return matches as token lists.
+ *
+ * @param json
+ * JSON-LD string with search and potential meta
+ * filters.
+ */
+ @Deprecated
+ public String searchTokenList (String json) {
+ if (DEBUG) {
+ jlog.debug(json);
+ }
+ if (index != null)
+ return new Krill(json).apply(index).toTokenListJsonString();
+ Result kr = new Result();
+ kr.addError(601, "Unable to find index");
+ return kr.toJsonString();
+ };
+
+
+ /**
+ * Get info on a match - by means of a richly annotated html
+ * snippet.
+ *
+ * @param id
+ * match id
+ * @param availabilityList
+ * @throws KustvaktException
+ */
+ public String getMatch (String id, Pattern licensePattern)
+ throws KustvaktException {
+ Match km;
+ if (index != null) {
+ try {
+ km = index.getMatch(id);
+ String availability = km.getAvailability();
+ checkAvailability(licensePattern, availability, id);
+ }
+ catch (QueryException qe) {
+ km = new Match();
+ km.addError(qe.getErrorCode(), qe.getMessage());
+ }
+ }
+ else {
+ km = new Match();
+ km.addError(601, "Unable to find index");
+ }
+ return km.toJsonString();
+ };
+
+
+ private void checkAvailability (Pattern licensePattern, String availability,
+ String id) throws KustvaktException {
+ if (DEBUG) {
+ jlog.debug("pattern: " + licensePattern.toString()
+ + ", availability: " + availability);
+ }
+ if (licensePattern != null && availability != null) {
+ Matcher m = licensePattern.matcher(availability);
+ if (!m.matches()) {
+ if (availability.isEmpty()) {
+ throw new KustvaktException(StatusCodes.MISSING_ATTRIBUTE,
+ "Availability for " + id + "is empty.", id);
+ }
+ throw new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Retrieving resource with ID " + id
+ + " is not allowed.",
+ id);
+ }
+ }
+
+ }
+
+
+ /*
+ * Retrieve the meta fields for a certain document
+ */
+ public String getFields (String id, List<String> fields,
+ Pattern licensePattern) throws KustvaktException {
+ MetaFields meta;
+
+ // No index found
+ if (index == null) {
+ meta = new MetaFields(id);
+ meta.addError(601, "Unable to find index");
+ }
+
+ // Index available
+ else if (fields != null) {
+ // Get fields
+ meta = index.getFields(id, fields);
+ }
+ else {
+ // Get fields
+ meta = index.getFields(id);
+ }
+
+ // EM: this approach forbids the whole metadata
+ // this should be refined by filtering out only the restricted
+ // metadata fields
+ // String availability = meta.getFieldValue("availability");
+ // checkAvailability(licensePattern, availability, id);
+
+ return meta.toJsonString();
+ };
+
+
+ public String getMatch (
+ String id, boolean info, List<String> foundries,
+ List<String> layers, boolean includeSpans,
+ boolean includeSnippet, boolean includeTokens,
+ boolean includeHighlights, boolean sentenceExpansion,
+ Pattern licensePattern) throws KustvaktException {
+ Match km;
+ if (index != null) {
+ try {
+ km = index.getMatchInfo(id, "tokens", info, foundries, layers,
+ includeSpans, includeSnippet, includeTokens,
+ includeHighlights, sentenceExpansion);
+ String availability = km.getAvailability();
+ checkAvailability(licensePattern, availability, id);
+ }
+ catch (QueryException qe) {
+ km = new Match();
+ km.addError(qe.getErrorCode(), qe.getMessage());
+ }
+ }
+ else {
+ km = new Match();
+ km.addError(601, "Unable to find index");
+ }
+ return km.toJsonString();
+ };
+
+
+ /**
+ * Get info on a match - by means of a richly annotated html
+ * snippet.
+ *
+ * @param id
+ * match id
+ * @param foundry
+ * the foundry of interest - may be null
+ * @param layer
+ * the layer of interest - may be null
+ * @param includeSpans
+ * Should spans be included (or only token infos)?
+ * @param includeHighlights
+ * Should highlight markup be included?
+ */
+ public String getMatch (String id, String foundry, String layer,
+ boolean includeSpans, boolean includeHighlights,
+ boolean sentenceExpansion) {
+
+ if (index != null) {
+ try {
+ /*
+ For multiple foundries/layers use
+ String idString,
+ "tokens",
+ true,
+ ArrayList<String> foundry,
+ ArrayList<String> layer,
+ boolean includeSpans,
+ boolean includeHighlights,
+ boolean extendToSentence
+ */
+ return index.getMatchInfo(id, "tokens", foundry, layer,
+ includeSpans, includeHighlights, sentenceExpansion)
+ .toJsonString();
+ }
+ catch (QueryException qe) {
+ Match km = new Match();
+ km.addError(qe.getErrorCode(), qe.getMessage());
+ return km.toJsonString();
+ }
+ };
+ Match km = new Match();
+ km.addError(601, "Unable to find index");
+ return km.toJsonString();
+ };
+
+
+ /**
+ * Get statistics on (virtual) collections.
+ *
+ * EM: might be changed later
+ *
+ * @param json
+ * JSON-LD string with potential meta filters.
+ * @throws KustvaktException
+ */
+ public String getStatistics (String json) throws KustvaktException {
+ if (index == null) {
+ return "{\"documents\" : -1, error\" : \"No index given\" }";
+ };
+
+ // Define a virtual corpus
+ KrillCollection kc;
+ if (json != null && !json.equals("")) {
+ if (DEBUG) {
+ jlog.debug(json);
+ }
+
+ // Create Virtual collection from json search
+ kc = new KrillCollection(json);
+ }
+
+ // There is no json string defined
+ else {
+
+ // Create Virtual collection of everything
+ kc = new KrillCollection();
+ };
+
+ // Set index
+ kc.setIndex(index);
+ long docs = 0, tokens = 0, sentences = 0, paragraphs = 0;
+ // Get numbers from index (currently slow)
+ try {
+ docs = kc.numberOf("documents");
+ if (docs > 0) {
+ tokens = kc.numberOf("tokens");
+ sentences = kc.numberOf("base/sentences");
+ paragraphs = kc.numberOf("base/paragraphs");
+ };
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ };
+
+ if (kc.hasErrors()) {
+ throw new KustvaktException(
+ "{\"errors\":" + kc.getErrors().toJsonString() + "}");
+ }
+ // Build json response
+ StringBuilder sb = new StringBuilder("{");
+ sb.append("\"documents\":").append(docs).append(",\"tokens\":")
+ .append(tokens).append(",\"sentences\":").append(sentences)
+ .append(",\"paragraphs\":").append(paragraphs).append("}");
+ return sb.toString();
+ };
+
+
+ /**
+ * Return the match identifier as a string.
+ * This is a convenient method to deal with legacy instantiation
+ * of the
+ * code.
+ */
+ public String getMatchId (String corpusID, String docID, String textID,
+ String matchID) {
+ // Create a string representation of the match
+ StringBuilder sb = new StringBuilder();
+ sb.append("match-").append(corpusID).append('/').append(docID)
+ .append('/').append(textID).append('-').append(matchID);
+ return sb.toString();
+ };
+
+
+ /**
+ * Return the text sigle as a string.
+ */
+ public String getTextSigle (String corpusID, String docID, String textID) {
+ // Create a string representation of the match
+ StringBuilder sb = new StringBuilder();
+ sb.append(corpusID).append('/').append(docID).append('/')
+ .append(textID);
+ return sb.toString();
+ };
+
+
+ /**
+ * Return the fingerprint of the latest index revision.
+ */
+ public String getIndexFingerprint () {
+ if (index != null) {
+ return index.getFingerprint();
+ };
+ return "null";
+ }
+
+
+ public JsonNode getFieldValuesForVC (String koralQuery, String fieldName) {
+ return new Krill().retrieveFieldValues(koralQuery, index, fieldName);
+ }
+
+};
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java b/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java
new file mode 100644
index 0000000..05d511b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/TRACE.java
@@ -0,0 +1,16 @@
+package de.ids_mannheim.korap.web;
+
+import javax.ws.rs.HttpMethod;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @author hanl
+ * @date 03/07/2014
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@HttpMethod("TRACE")
+public @interface TRACE {}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java
new file mode 100644
index 0000000..853557a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/APIVersionFilter.java
@@ -0,0 +1,44 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.core.PathSegment;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+
+/**
+ * Checks API version in URL path.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+@Priority(Integer.MIN_VALUE)
+public class APIVersionFilter
+ implements ContainerRequestFilter {
+
+ @Autowired
+ private KustvaktConfiguration config;
+
+ public void filter (ContainerRequestContext request) {
+ List<PathSegment> pathSegments = request.getUriInfo().getPathSegments();
+ String version = pathSegments.get(0).getPath();
+
+ if (!config.getSupportedVersions().contains(version)) {
+ throw new NotFoundException();
+ // throw kustvaktResponseHandler.throwit(
+ // new
+ // KustvaktException(StatusCodes.UNSUPPORTED_API_VERSION,
+ // "API " + version + " is unsupported.", version));
+ }
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
new file mode 100644
index 0000000..885ce41
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AdminFilter.java
@@ -0,0 +1,78 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.servlet.ServletContext;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.SecurityContext;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.dao.AdminDao;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.utils.JerseyUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * Verifies admin credentials or token before allowing access to
+ * administrative services
+ *
+ * @author hanl, margaretha
+ *
+ * @see {@link AuthenticationFilter}
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class AdminFilter extends AuthenticationFilter {
+
+ private @Context ServletContext servletContext;
+ @Autowired
+ private AdminDao adminDao;
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Override
+ public void filter (ContainerRequestContext context) {
+ super.filter(context);
+ String username = "guest";
+ String adminToken = JerseyUtils.getFormParameters(context).asMap()
+ .getFirst("token");
+ if (!checkAdminToken(adminToken)) {
+ SecurityContext securityContext = context.getSecurityContext();
+ TokenContext tokenContext = (TokenContext) securityContext
+ .getUserPrincipal();
+ checkAdminCredentials(tokenContext, username);
+ }
+ }
+
+
+ private boolean checkAdminToken (String adminToken) {
+ if (adminToken != null && !adminToken.isEmpty()) {
+ if (adminToken
+ .equals(servletContext.getInitParameter("adminToken"))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private void checkAdminCredentials (TokenContext tokenContext,
+ String username) {
+ if (tokenContext != null) {
+ username = tokenContext.getUsername();
+ if (adminDao.isAdmin(username)) {
+ return;
+ }
+ }
+
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username));
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
new file mode 100644
index 0000000..bb5d6c1
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/AuthenticationFilter.java
@@ -0,0 +1,127 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.glassfish.jersey.server.ContainerRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+import de.ids_mannheim.korap.authentication.http.AuthorizationData;
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.utils.TimeUtils;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/** Authentication filter extracts an authentication token from
+ * authorization header and uses an authentication provider
+ * with respect to the token type to create a token context as
+ * a security context.
+ *
+ * @author hanl, margaretha
+ * @date 28/01/2014
+ * @last update 12/2017
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class AuthenticationFilter
+ implements ContainerRequestFilter {
+
+ private static Logger jlog = LogManager.getLogger(AuthenticationFilter.class);
+
+ @Autowired
+ private HttpAuthorizationHandler authorizationHandler;
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ String host = request.getHeaderString(ContainerRequest.HOST);
+ String ua = request.getHeaderString(ContainerRequest.USER_AGENT);
+
+ String authorization =
+ request.getHeaderString(ContainerRequest.AUTHORIZATION);
+
+ if (authorization != null && !authorization.isEmpty()) {
+ TokenContext context = null;
+ AuthorizationData authData;
+ try {
+ authData = authorizationHandler
+ .parseAuthorizationHeaderValue(authorization);
+
+ switch (authData.getAuthenticationScheme()) {
+ // EM: For testing only, must be disabled for
+ // production
+ case BASIC:
+ context = authenticationManager.getTokenContext(
+ TokenType.BASIC, authData.getToken(), host, ua);
+ break;
+ // EM: has not been tested yet
+ // case SESSION:
+ // context =
+ // authenticationManager.getTokenContext(
+ // TokenType.SESSION, authData.getToken(), host,
+ // ua);
+ // break;
+
+ // OAuth2 authentication scheme
+ case BEARER:
+ context = authenticationManager.getTokenContext(
+ TokenType.BEARER, authData.getToken(), host,
+ ua);
+ break;
+ // EM: JWT token-based authentication scheme
+ case API:
+ jlog.warn("Authentication filter using token API");
+ throw new KustvaktException(
+ StatusCodes.AUTHENTICATION_FAILED,
+ "Authentication API is no longer supported.");
+ default:
+ throw new KustvaktException(
+ StatusCodes.AUTHENTICATION_FAILED,
+ "Authentication scheme is not supported.");
+ }
+ checkContext(context, request);
+ request.setSecurityContext(new KustvaktContext(context));
+ }
+ catch (KustvaktException e) {
+ throw kustvaktResponseHandler.throwit(e);
+ }
+ }
+ }
+
+
+ private void checkContext (TokenContext context, ContainerRequestContext request)
+ throws KustvaktException {
+ if (context == null) {
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+ "Context is null.");
+ }
+ else if (!context.isValid()) {
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+ "Context is not valid: "
+ + "missing username, password or authentication scheme.");
+ }
+ else if (context.isSecureRequired() && !request.getSecurityContext().isSecure()) {
+ throw new KustvaktException(StatusCodes.AUTHENTICATION_FAILED,
+ "Request is not secure.");
+ }
+ else if (TimeUtils.isExpired(context.getExpirationTime())) {
+ throw new KustvaktException(StatusCodes.EXPIRED,
+ "Access token is expired");
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
new file mode 100644
index 0000000..bb14e10
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/BlockingFilter.java
@@ -0,0 +1,50 @@
+package de.ids_mannheim.korap.web.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * @author hanl
+ * @date 11/12/2014
+ * <p/>
+ * endpoint filter to block access to an endpoint, in case no
+ * anonymous access should be allowed!
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class BlockingFilter implements ContainerRequestFilter {
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ TokenContext context;
+
+ SecurityContext securityContext = request.getSecurityContext();
+ if (securityContext != null) {
+ context = (TokenContext) securityContext.getUserPrincipal();
+ }
+ else {
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.UNSUPPORTED_OPERATION));
+ }
+
+ if (context == null || context.isDemo()) {
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: guest", "guest"));
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java
new file mode 100644
index 0000000..e020a8d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoFilter.java
@@ -0,0 +1,51 @@
+package de.ids_mannheim.korap.web.filter;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import org.glassfish.jersey.server.ContainerRequest;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+
+/**
+ * @author hanl
+ * @date 08/02/2016
+ */
+@Priority(Priorities.AUTHENTICATION)
+public class DemoFilter implements ContainerRequestFilter {
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ String authentication =
+ request.getHeaderString(ContainerRequest.AUTHORIZATION);
+ if (authentication == null || authentication.isEmpty()) {
+ if (request.getSecurityContext() == null) {
+ request.setSecurityContext(createContext());
+ }
+ }
+ }
+
+
+ private SecurityContext createContext () {
+ TokenContext context = new TokenContext();
+ String token = null;
+ try {
+ token = HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("demo", "demo2015");
+ }
+ catch (KustvaktException e) {
+ e.printStackTrace();
+ }
+ context.setToken(token);
+ context.setTokenType(TokenType.BASIC);
+ context.setUsername("demo");
+ return new KustvaktContext(context);
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
new file mode 100644
index 0000000..22b43f0
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/DemoUserFilter.java
@@ -0,0 +1,69 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.security.Principal;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
+
+import org.glassfish.jersey.server.ContainerRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.constant.TokenType;
+import de.ids_mannheim.korap.security.context.KustvaktContext;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.utils.TimeUtils;
+
+/**
+ * Created by hanl on 7/15/14.
+ */
+@Component
+@Priority(Priorities.AUTHENTICATION)
+public class DemoUserFilter implements ContainerRequestFilter {
+
+ @Context
+ UriInfo info;
+ @Autowired
+ private KustvaktConfiguration config;
+
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ String host = request.getHeaderString(ContainerRequest.HOST);
+ String ua = request.getHeaderString(ContainerRequest.USER_AGENT);
+ String authentication = request
+ .getHeaderString(ContainerRequest.AUTHORIZATION);
+
+ // means that this is the public service
+ if (authentication == null || authentication.isEmpty()) {
+ Principal pr = null;
+ SecurityContext securityContext = request.getSecurityContext();
+ if (securityContext != null) {
+ pr = securityContext.getUserPrincipal();
+ }
+ if (pr == null)
+ request.setSecurityContext(new KustvaktContext(
+ createShorterToken(host, ua)));
+ }
+ }
+
+
+ private TokenContext createShorterToken (String host, String agent) {
+ User demo = User.UserFactory.getDemoUser();
+ TokenContext c = new TokenContext();
+ c.setUsername(demo.getUsername());
+ c.setHostAddress(host);
+ c.setUserAgent(agent);
+ c.setExpirationTime(
+ TimeUtils.plusSeconds(config.getShortTokenTTL()).getMillis());
+ c.setTokenType(TokenType.BASIC);
+ return c;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
new file mode 100644
index 0000000..c4b539a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/NonDemoBlockingFilter.java
@@ -0,0 +1,52 @@
+package de.ids_mannheim.korap.web.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.core.SecurityContext;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.web.KustvaktResponseHandler;
+
+/**
+ * EM: pretty much identical to {@link BlockingFilter}, should be deleted?
+ *
+ * @author hanl
+ * @date 11/12/2014
+ * <p/>
+ * endpoint filter to block access to an endpoint, in case no
+ * anonymous access should be allowed!
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class NonDemoBlockingFilter
+ implements ContainerRequestFilter {
+
+ @Autowired
+ private KustvaktResponseHandler kustvaktResponseHandler;
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ TokenContext context;
+ SecurityContext securityContext = request.getSecurityContext();
+ if (securityContext != null) {
+ context = (TokenContext) securityContext.getUserPrincipal();
+ }
+ else {
+ throw kustvaktResponseHandler.throwit(new KustvaktException(
+ StatusCodes.UNSUPPORTED_OPERATION));
+ }
+
+ if (context == null || context.isDemo()) {
+ throw kustvaktResponseHandler.throwit(
+ new KustvaktException(StatusCodes.AUTHORIZATION_FAILED,
+ "Operation is not permitted for guest users"));
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java b/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java
new file mode 100644
index 0000000..5a9f19a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/filter/PiwikFilter.java
@@ -0,0 +1,146 @@
+package de.ids_mannheim.korap.web.filter;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+
+import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.logging.LoggingFeature;
+
+import de.ids_mannheim.korap.authentication.AuthenticationManager;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.security.context.TokenContext;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.user.UserSettingProcessor;
+import de.ids_mannheim.korap.user.Userdata;
+import net.minidev.json.JSONArray;
+
+/**
+ * @author hanl
+ * @date 13/05/2014
+ */
+@Component
+@Priority(Priorities.AUTHORIZATION)
+public class PiwikFilter implements ContainerRequestFilter {
+
+ private WebTarget service;
+ // private static final String SERVICE = "http://localhost:8888";
+ private static final String SERVICE = "http://10.0.10.13";
+ private static Logger jlog = LogManager.getLogger(PiwikFilter.class);
+ public static boolean ENABLED = false;
+ private Map<String, String> customVars;
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+
+ public PiwikFilter () {
+// controller = BeansFactory.getKustvaktContext()
+// .getAuthenticationManager();
+ ClientConfig clientConfig = new ClientConfig();
+ if (jlog.isDebugEnabled())
+ clientConfig.register(LoggingFeature.class);
+ Client client = ClientBuilder.newClient(clientConfig);
+ UriBuilder b = UriBuilder.fromUri(SERVICE);
+ service = client.target(b.build());
+ this.customVars = new HashMap<>();
+ }
+
+
+ private void send (ContainerRequestContext request) {
+ Random random = new SecureRandom();
+ Locale l = null;
+ if (request.getAcceptableLanguages() != null)
+ l = request.getAcceptableLanguages().get(0);
+ try {
+ service.path("piwik/piwik.php")
+ .queryParam("idsite", "2")
+ .queryParam("rec", "1")
+ //todo check for empty container
+ .queryParam("_cvar", translateCustomData())
+ .queryParam("cip", request.getHeaderString("Host"))
+ .queryParam("cookie", "false")
+ .queryParam("r", String.valueOf(random.nextDouble()))
+ .queryParam("action_name",
+ request.getUriInfo().getRequestUri().toASCIIString())
+ .request()
+ .accept("text/html")
+ .header("Host", request.getHeaderString("Host"))
+ .header("User-Agent", request.getHeaderString("User-Agent"))
+ .acceptLanguage(l).method("GET");
+ }
+ catch (Exception e) {
+ // do nothing if piwik not available!
+ }
+ }
+
+
+ private String translateCustomData () {
+ final Map<String, List<String>> customVariables = new HashMap<String, List<String>>();
+ int i = 0;
+ for (final Map.Entry<String, String> entry : this.customVars.entrySet()) {
+ i++;
+ final List<String> list = new ArrayList<String>();
+ list.add(entry.getKey());
+ list.add(entry.getValue());
+ customVariables.put(Integer.toString(i), list);
+ }
+
+ final JSONArray json = new JSONArray();
+ json.add(customVariables);
+
+ // remove unnecessary parent square brackets from JSON-string
+ String jsonString = json.toString().substring(1,
+ json.toString().length() - 1);
+ customVars.clear();
+ return jsonString;
+ }
+
+
+ @Override
+ public void filter (ContainerRequestContext request) {
+ if (ENABLED) {
+ try {
+ TokenContext context;
+ SecurityContext securityContext = request.getSecurityContext();
+ if (securityContext != null) {
+ context = (TokenContext) securityContext.getUserPrincipal();
+
+ if (context.getUsername() != null){
+ // since this is cached, not very expensive!
+ User user = authenticationManager.getUser(context.getUsername());
+ Userdata data = authenticationManager
+ .getUserData(user, UserSettingProcessor.class);
+ if ((Boolean) data.get(Attributes.COLLECT_AUDITING_DATA))
+ customVars.put("username", context.getUsername());
+ }
+ }
+ }
+ catch (KustvaktException e) {
+ //do nothing
+ }
+ send(request);
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java
new file mode 100644
index 0000000..12c7825
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/FormRequestWrapper.java
@@ -0,0 +1,94 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class to wrapp multivaluedmap into a hashmap. Depending on
+ * the strict parameter,
+ * list values are retained in the resulting wrapper map.
+ *
+ * @author hanl
+ * @date 25/04/2015
+ */
+public class FormRequestWrapper extends HttpServletRequestWrapper {
+
+ private MultivaluedMap<String, String> form;
+
+
+ /**
+ * Constructs a request object wrapping the given request.
+ *
+ * @param request
+ * @throws IllegalArgumentException
+ * if the request is null
+ */
+ public FormRequestWrapper (HttpServletRequest request,
+ MultivaluedMap<String, String> form) {
+ super(request);
+ this.form = form;
+ }
+
+
+ @Override
+ public String getParameter (String name) {
+ String value = super.getParameter(name);
+ if (value == null){
+ value = form.getFirst(name);
+ }
+ return value;
+ }
+
+
+ @Override
+ public String[] getParameterValues (String name) {
+ String[] values = super.getParameterValues(name);
+ if (values == null && form.get(name) != null) {
+ values = new String[form.get(name).size()];
+ values = form.get(name).toArray(values);
+ }
+ return values;
+ }
+
+
+ public HashMap<String, Object> singleValueMap () {
+ return toMap(this.form, false);
+ }
+
+
+ /**
+ * @param strict
+ * returns only values with size equal to one. If false
+ * pairs key to first value
+ * in value list and returns the result
+ * @return key/value map
+ */
+ public static HashMap<String, Object> toMap (
+ MultivaluedMap<String, String> form, boolean strict) {
+ if (form == null)
+ return null;
+ HashMap<String, Object> map = new HashMap<>();
+ for (String key : form.keySet()) {
+ if (strict && form.get(key).size() > 1)
+ continue;
+ map.put(key, form.getFirst(key));
+
+ }
+ return map;
+ }
+
+
+ public void put (String key, String value) {
+ this.form.putSingle(key, value);
+ }
+
+
+ public void put (String key, String ... values) {
+ this.form.put(key, Arrays.<String> asList(values));
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java
new file mode 100644
index 0000000..543fbae
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/HTMLBuilder.java
@@ -0,0 +1,60 @@
+package de.ids_mannheim.korap.web.utils;
+
+/**
+ * @author hanl
+ * @date 12/04/2014
+ */
+public class HTMLBuilder {
+
+ private StringBuilder html;
+ private StringBuilder body;
+ private String bodyAttr;
+
+
+ public HTMLBuilder () {
+ html = new StringBuilder();
+ body = new StringBuilder();
+ bodyAttr = "";
+ html.append("<html>");
+ }
+
+
+ public void addHeader (String header, int h) {
+ html.append("<h" + h + ">");
+ html.append(header);
+ html.append("</h" + h + ">");
+ }
+
+
+ public void addToBody (String body) {
+ this.body.append(body);
+ }
+
+
+ public void addToBody (String body, String attributes) {
+ this.body.append(body);
+ bodyAttr = attributes;
+ }
+
+
+ public String build () {
+ if (bodyAttr.isEmpty())
+ html.append("<body>");
+ else {
+ html.append("<body ");
+ html.append(bodyAttr);
+ html.append(">");
+ }
+
+ html.append(body);
+ html.append("</body>");
+ html.append("</html>");
+ return html.toString();
+ }
+
+
+ @Override
+ public String toString () {
+ return build();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java
new file mode 100644
index 0000000..126f34d
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/JsonExceptionMapper.java
@@ -0,0 +1,36 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.annotation.Priority;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.web.CoreResponseHandler;
+
+/**
+ * Creates appropriate responses in case of incorrect JSON
+ * deserialization or JsonMappingException, for instance when a
+ * request parameter should be deserialized as an ENUM but the
+ * parameter value does not match any of the available ENUM values.
+ *
+ * @author margaretha
+ *
+ */
+@Priority(value = 1)
+@Provider
+public class JsonExceptionMapper
+ implements ExceptionMapper<JsonMappingException> {
+
+ @Override
+ public Response toResponse (JsonMappingException exception) {
+ String entity = CoreResponseHandler.buildNotification(
+ StatusCodes.DESERIALIZATION_FAILED, exception.getMessage(),
+ null);
+ return Response.status(Response.Status.BAD_REQUEST).entity(entity)
+ .build();
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java
new file mode 100644
index 0000000..8e9de8a
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktMap.java
@@ -0,0 +1,79 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author hanl
+ * @date 21/01/2016
+ */
+public class KustvaktMap {
+
+ private boolean monoTyped;
+ private Map<String, Object> values;
+
+
+ public KustvaktMap () {
+ this.values = new HashMap<>();
+ this.monoTyped = false;
+ }
+
+
+ public KustvaktMap (Map<String, Object> m) {
+ this();
+ setMap(m);
+ }
+
+
+ public void setMap (Map<String, Object> m) {
+ if (!isGeneric(m) | !this.monoTyped)
+ this.values.putAll(m);
+ }
+
+
+ public boolean isGeneric () {
+ return !this.monoTyped && isGeneric(this.values);
+ }
+
+
+ private static boolean isGeneric (Map<String, Object> map) {
+ int i = 0;
+ for (Object o : map.values()) {
+ if (o instanceof String)
+ i++;
+ }
+ return !(i == map.size());
+ }
+
+
+ public void setMonoValue (boolean monovalue) {
+ this.monoTyped = monovalue;
+ }
+
+
+ public String get (String key) {
+ Object o = this.values.get(key);
+ if (!isGeneric())
+ return (String) o;
+ return String.valueOf(o);
+ }
+
+
+ public Object getRaw (String key) {
+ return this.values.get(key);
+ }
+
+
+ public <T extends Object> Object get (String key, Class<T> cl) {
+ if (isGeneric())
+ return (T) this.values.get(key);
+ return get(key);
+ }
+
+
+ public Set<String> keySet () {
+ return this.values.keySet();
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java
new file mode 100644
index 0000000..b43adf8
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/KustvaktResponseBuilder.java
@@ -0,0 +1,33 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author hanl
+ * @date 20/01/2016
+ */
+public class KustvaktResponseBuilder {
+ Map<String, Object> _values;
+
+
+ public KustvaktResponseBuilder () {
+ this._values = new HashMap<>();
+ }
+
+
+ public KustvaktResponseBuilder addEntity (Object o) {
+ if (o instanceof Map && !((Map) o).isEmpty())
+ this._values.putAll((Map<? extends String, ?>) o);
+
+
+ return this;
+ }
+
+
+ @Override
+ public String toString () {
+ return "";
+ }
+
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java
new file mode 100644
index 0000000..cb72ba5
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/LocaleFactory.java
@@ -0,0 +1,25 @@
+package de.ids_mannheim.korap.web.utils;
+
+import org.glassfish.hk2.api.Factory;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.ext.Provider;
+import java.util.List;
+import java.util.Locale;
+
+@Provider
+public class LocaleFactory implements Factory<Locale> {
+ @Context
+ private ContainerRequestContext context;
+
+ @Override
+ public Locale provide() {
+ final List<Locale> locales = context.getAcceptableLanguages();
+ if (locales.isEmpty())
+ return Locale.US;
+ return locales.get(0);
+ }
+
+ @Override
+ public void dispose(Locale instance) {}
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java
new file mode 100644
index 0000000..a05983b
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/NotFoundMapper.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.ws.rs.NotFoundException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ResourceContext;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eclipse.jetty.http.HttpStatus;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+
+/**
+ * Handles not found API version by redirecting the request URI to a
+ * similar URI with current API version.
+ *
+ * @author margaretha
+ *
+ */
+@Component
+@Provider
+public class NotFoundMapper implements ExceptionMapper<NotFoundException> {
+
+ private static Logger jlog = LogManager.getLogger(NotFoundMapper.class);
+ public static final Pattern VERSION_PATTERN =
+ Pattern.compile("/(v[0-9][^/]*)(/.*)");
+ private static final boolean DEBUG = false;
+
+ @Autowired
+ private KustvaktConfiguration config;
+
+ @Context
+ private ResourceContext resourceContext;
+
+ @Override
+ public Response toResponse (NotFoundException exception) {
+ ContainerRequestContext requestContext =
+ resourceContext.getResource(ContainerRequestContext.class);
+
+ URI notFoundUri = requestContext.getUriInfo().getRequestUri();
+
+ String path = notFoundUri.getPath();
+ String baseUrl = config.getBaseURL();
+ baseUrl = baseUrl.substring(0, baseUrl.length() - 2);
+
+ if (path.startsWith(baseUrl)) {
+ path = path.substring(baseUrl.length(), path.length());
+ Matcher matcher = VERSION_PATTERN.matcher(path);
+ if (!matcher.matches()) {
+ path = baseUrl + "/" + config.getCurrentVersion() + path;
+ URI redirectUri = UriBuilder.fromUri(notFoundUri)
+ .replacePath(path).build();
+ if (DEBUG) {
+ jlog.debug("REDIRECT: " + redirectUri.toString());
+ }
+ return Response.status(HttpStatus.PERMANENT_REDIRECT_308)
+ .location(redirectUri).build();
+ }
+ else if (!matcher.group(1).equals(config.getCurrentVersion())) {
+ path = baseUrl + "/" + config.getCurrentVersion()
+ + matcher.group(2);
+ URI redirectUri = UriBuilder.fromUri(notFoundUri)
+ .replacePath(path).build();
+ if (DEBUG) {
+ jlog.debug("REDIRECT replace: " + redirectUri.toString());
+ }
+ return Response.status(HttpStatus.PERMANENT_REDIRECT_308)
+ .location(redirectUri).build();
+ }
+ }
+ return Response.status(HttpStatus.NOT_FOUND_404).build();
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java
new file mode 100644
index 0000000..ab0d4ea
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFilters.java
@@ -0,0 +1,23 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the list of {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes associated with a resource method.
+ * <p>
+ * This annotation can be specified on a class or on method(s). Specifying it
+ * at a class level means that it applies to all the methods in the class.
+ * Specifying it on a method means that it is applicable to that method only.
+ * If applied at both the class and methods level , the method value overrides
+ * the class value.
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ResourceFilters {
+ Class<?>[] value();
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java
new file mode 100644
index 0000000..62b7745
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ResourceFiltersFeature.java
@@ -0,0 +1,27 @@
+package de.ids_mannheim.korap.web.utils;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+/**
+ * Registers {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes for a resource method annotated with {@link ResourceFilters}.
+ */
+@Provider
+public class ResourceFiltersFeature implements DynamicFeature {
+
+ @Override
+ public void configure (ResourceInfo resourceInfo, FeatureContext context) {
+ ResourceFilters filtersAnnotation = resourceInfo.getResourceMethod().getAnnotation(ResourceFilters.class);
+ if (filtersAnnotation == null)
+ filtersAnnotation = resourceInfo.getResourceClass().getAnnotation(ResourceFilters.class);
+
+ if (filtersAnnotation != null) {
+ for (Class<?> filter : filtersAnnotation.value())
+ context.register(filter);
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java
new file mode 100644
index 0000000..12dbf57
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFilters.java
@@ -0,0 +1,22 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Defines the list of {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes associated with a resource method.
+ * <p>
+ * This annotation can be specified on a class or on method(s). Specifying it
+ * at a class level means that it applies to all the methods in the class.
+ * Specifying it on a method means that it is applicable to that method only.
+ * If applied at both the class and methods level , the method value overrides
+ * the class value.
+ */
+@Target({ ElementType.TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SearchResourceFilters {
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java
new file mode 100644
index 0000000..45a7c4e
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/SearchResourceFiltersFeature.java
@@ -0,0 +1,58 @@
+package de.ids_mannheim.korap.web.utils;
+
+import java.util.List;
+
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import javax.ws.rs.ext.Provider;
+
+import org.glassfish.jersey.model.internal.CommonConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import de.ids_mannheim.korap.web.filter.APIVersionFilter;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+/**
+ * Registers {@link javax.ws.rs.container.ContainerRequestFilter}
+ * and {@link javax.ws.rs.container.ContainerResponseFilter}
+ * classes for a resource method annotated with {@link ResourceFilters}.
+ */
+@Provider
+@Component
+public class SearchResourceFiltersFeature implements DynamicFeature {
+
+ @Value("${search.resource.filters:AuthenticationFilter,DemoUserFilter}")
+ private String[] resourceFilters;
+
+ @Override
+ public void configure (ResourceInfo resourceInfo, FeatureContext context) {
+ SearchResourceFilters filters = resourceInfo.getResourceMethod()
+ .getAnnotation(SearchResourceFilters.class);
+ if (filters != null) {
+ CommonConfig con = (CommonConfig) context.getConfiguration();
+ con.getComponentBag().clear();
+ }
+ else {
+ filters = resourceInfo.getResourceClass()
+ .getAnnotation(SearchResourceFilters.class);
+ }
+
+ if (filters != null) {
+ List<?> list = Arrays.asList(resourceFilters);
+ if (!list.contains("APIVersionFilter")) {
+ context.register(APIVersionFilter.class);
+ }
+
+ for(String c : resourceFilters) {
+ try {
+ context.register(Class.forName("de.ids_mannheim.korap.web.filter." + c));
+ }
+ catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java b/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java
new file mode 100644
index 0000000..de34e38
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/web/utils/ShutdownHook.java
@@ -0,0 +1,29 @@
+package de.ids_mannheim.korap.web.utils;
+
+/**
+ * @author hanl
+ * @date 18/02/2014
+ */
+public class ShutdownHook extends Thread {
+
+ @Override
+ public void run () {
+ // Properties config = ExtensionBeans.getInstance().getConfiguration()
+ // .getMailProperties();
+
+ // Email e = new SimpleEmail();
+ // try {
+ // e.setHostName(config.getProperty("mail.host"));
+ // e.setSmtpPort(587);
+ // e.setSubject("KorAP Rest service shutdown Notification");
+ // e.setFrom(config.getProperty("mail.from"));
+ // e.addTo("hanl@ids-mannheim.de");
+ // e.setMsg("The KorAP - REST application shut down unexpectedly!!");
+ // e.send();
+ // } catch (EmailException e1) {
+ // e1.printStackTrace();
+ // }
+
+ }
+
+}
diff --git a/full/src/main/resources/annotation-scripts/foundries/base.js b/full/src/main/resources/annotation-scripts/foundries/base.js
new file mode 100644
index 0000000..a21cce6
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/base.js
@@ -0,0 +1,15 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["Base Annotation", "base/", "Structure"]
+ );
+
+ ah["base/"] = [
+ ["Structure", "s="]
+ ];
+
+ ah["base/s="] = [
+ ["s", "s", "Sentence"],
+ ["p", "p", "Paragraph"],
+ ["t", "t", "Text"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/cnx.js b/full/src/main/resources/annotation-scripts/foundries/cnx.js
new file mode 100644
index 0000000..9316cbd
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/cnx.js
@@ -0,0 +1,63 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["Connexor", "cnx/", "Constituency, Lemma, Morphology, Part-of-Speech, Syntax"]
+ );
+
+ ah["cnx/"] = [
+ ["Constituency", "c="],
+ ["Lemma", "l="],
+ ["Morphology", "m="],
+ ["Part-of-Speech", "p="],
+ ["Syntax", "syn="]
+ ];
+
+ ah["cnx/c="] = [
+ ["np", "np ", "Nominal Phrase"]
+ ];
+
+ // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
+ ah["cnx/m="] = [
+ ["Abbr","Abbr ", "Nouns: Abbreviation"],
+ ["CMP","CMP ", "Adjective: Comparative"],
+ ["IMP", "IMP ", "Mood: Imperative"],
+ ["IND", "IND ", "Mood: Indicative"],
+ ["INF", "INF ", "Infinitive"],
+ ["ORD","ORD ", "Numeral: Ordinal"],
+ ["PAST", "PAST ", "Tense: past"],
+ ["PCP", "PCP ", "Participle"],
+ ["PERF", "PERF ", "Perfective Participle"],
+ ["PL","PL ", "Nouns: Plural"],
+ ["PRES", "PRES ", "Tense: present"],
+ ["PROG", "PROG ", "Progressive Participle"],
+ ["Prop","Prop ", "Nouns: Proper Noun"],
+ ["SUB", "SUB ", "Mood: Subjunctive"],
+ ["SUP","SUP ", "Adjective: Superlative"]
+ ];
+
+ // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/morph.html
+ ah["cnx/p="] = [
+ ["A", "A ", "Adjective"],
+ ["ADV", "ADV ", "Adverb"],
+ ["CC", "CC ", "Coordination Marker"],
+ ["CS", "CS ", "Clause Marker"],
+ ["DET", "DET ", "Determiner"],
+ ["INTERJ", "INTERJ ", "Interjection"],
+ ["N", "N ", "Noun"],
+ ["NUM", "NUM ", "Numeral"],
+ ["PREP", "PREP ", "Preposition"],
+ ["PRON", "PRON ", "Pro-Nominal"],
+ ["V", "V ", "Verb"]
+ ];
+
+ // http://www.ids-mannheim.de/cosmas2/projekt/referenz/connexor/syntax.html
+ ah["cnx/syn="] = [
+ ["@ADVL", "@ADVL ", "Adverbial Head"],
+ ["@AUX", "@AUX ", "Auxiliary Verb"],
+ ["@CC", "@CC ", "Coordination"]
+ ["@MAIN", "@MAIN ", "Main Verb"],
+ ["@NH", "@NH ", "Nominal Head"],
+ ["@POSTMOD", "@POSTMOD ", "Postmodifier"],
+ ["@PREMARK", "@PREMARK ", "Preposed Marker"],
+ ["@PREMOD", "@PREMOD ", "Premodifier"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/corenlp.js b/full/src/main/resources/annotation-scripts/foundries/corenlp.js
new file mode 100644
index 0000000..ff3b44a
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/corenlp.js
@@ -0,0 +1,1374 @@
+define(["hint/foundries","hint/foundries/stts","hint/foundries/negranodes","hint/foundries/negraedges"],
+ function (ah, sttsArray, negraNodesArray, negraEdgesArray) {
+
+ ah["-"].push(
+ ["CoreNLP", "corenlp/", "Constituency, Named Entities, Part-of-Speech"]
+ );
+
+ ah["corenlp/"] = [
+ ["Constituency", "c="],
+ ["Named Entity", "ne=" , "Combined"],
+ ["Named Entity", "ne_dewac_175m_600=" , "ne_dewac_175m_600"],
+ ["Named Entity", "ne_hgc_175m_600=", "ne_hgc_175m_600"],
+ ["Part-of-Speech", "p="]
+ ];
+
+ ah["corenlp/ne="] = [
+ ["I-LOC", "I-LOC ", "Location"],
+ ["I-MISC", "I-MISC ", "Miscellaneous"],
+ ["I-ORG", "I-ORG ", "Organization"],
+ ["I-PER", "I-PER ", "Person"]
+ ];
+
+ ah["corenlp/ne_dewac_175m_600="] = [
+ ["I-LOC", "I-LOC ", "Location"],
+ ["I-MISC", "I-MISC ", "Miscellaneous"],
+ ["I-ORG", "I-ORG ", "Organization"],
+ ["I-PER", "I-PER ", "Person"]
+ ];
+
+ ah["corenlp/ne_hgc_175m_600="] = [
+ ["I-LOC", "I-LOC ", "Location"],
+ ["I-MISC", "I-MISC ", "Miscellaneous"],
+ ["I-ORG", "I-ORG ", "Organization"],
+ ["I-PER", "I-PER ", "Person"]
+ ];
+
+ ah["corenlp/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"]
+ ];
+
+ ah["corenlp/c="] = [
+ ["AA", "AA", "superlative phrase with 'am'"],
+ ["AP","AP", "adjektive phrase"],
+ ["AVP","AVP", "adverbial phrase"],
+ ["CAP","CAP", "coordinated adjektive phrase"],
+ ["CAVP","CAVP", "coordinated adverbial phrase"],
+ ["CAC","CAC", "coordinated adposition"],
+ ["CCP","CCP", "coordinated complementiser"],
+ ["CH","CH", "chunk"],
+ ["CNP","CNP", "coordinated noun phrase"],
+ ["CO","CO", "coordination"],
+ ["CPP","CPP", "coordinated adpositional phrase"],
+ ["CS","CS", "coordinated sentence"],
+ ["CVP","CVP", "coordinated verb phrase (non-finite)"],
+ ["CVZ","CVZ", "coordinated zu-marked infinitive"],
+ ["DL","DL", "discourse level constituent"],
+ ["ISU","ISU", "idiosyncratis unit"],
+ ["MPN","MPN", "multi-word proper noun"],
+ ["MTA","MTA", "multi-token adjective"],
+ ["NM","NM", "multi-token number"],
+ ["NP","NP", "noun phrase"],
+ ["PP","PP", "adpositional phrase"],
+ ["QL","QL", "quasi-languag"],
+ ["ROOT","ROOT", "root node"],
+ ["S","S", "sentence"],
+ ["VP","VP", "verb phrase (non-finite)"],
+ ["VZ","VZ", "zu-marked infinitive"]
+ ];
+
+ ah["corenlp/c=AA-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=AP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=AVP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CAP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CAVP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CAC-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CCP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CH-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CNP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CO-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CPP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CS-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CVP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=CVZ-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=DL-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=ISU-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=MPN-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=MTA-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=NM-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=NP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=PP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=QL-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=ROOT-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=S-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=VP-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+ ah["corenlp/c=VZ-"] = [
+ ["AC", "AC ", "adpositional case marker"],
+ ["ADC", "ADC ", "adjective component"],
+ ["AMS", "AMS ", "measure argument of adj"],
+ ["APP", "APP ", "apposition"],
+ ["AVC", "AVC ", "adverbial phrase component"],
+ ["CC", "CC ", "comparative complement"],
+ ["CD", "CD ", "coordinating conjunction"],
+ ["CJ", "CJ ", "conjunct"],
+ ["CM", "CM ", "comparative concjunction"],
+ ["CP", "CP ", "complementizer"],
+ ["DA", "DA ", "dative"],
+ ["DH", "DH ", "discourse-level head"],
+ ["DM", "DM ", "discourse marker"],
+ ["GL", "GL ", "prenominal genitive"],
+ ["GR", "GR ", "postnominal genitive"],
+ ["HD", "HD ", "head"],
+ ["JU", "JU ", "junctor"],
+ ["MC", "MC ", "comitative"],
+ ["MI", "MI ", "instrumental"],
+ ["ML", "ML ", "locative"],
+ ["MNR", "MNR ", "postnominal modifier"],
+ ["MO", "MO ", "modifier"],
+ ["MR", "MR ", "rhetorical modifier"],
+ ["MW", "MW ", "way (directional modifier)"],
+ ["NG", "NG ", "negation"],
+ ["NK", "NK ", "noun kernel modifier"],
+ ["NMC", "NMC ", "numerical component"],
+ ["OA", "OA ", "accusative object"],
+ ["OA2", "OA2 ", "second accusative object"],
+ ["OC", "OC ", "clausal object"],
+ ["OG", "OG ", "genitive object"],
+ ["PD", "PD ", "predicate"],
+ ["PG", "PG ", "pseudo-genitive"],
+ ["PH", "PH ", "placeholder"],
+ ["PM", "PM ", "morphological particle"],
+ ["PNC", "PNC ", "proper noun component"],
+ ["RC", "RC ", "relative clause"],
+ ["RE", "RE ", "repeated element"],
+ ["RS", "RS ", "reported speech"],
+ ["SB", "SB ", "subject"],
+ ["SBP", "SBP ", "passivised subject (PP)"],
+ ["SP", "SP ", "subject or predicate"],
+ ["SVP", "SVP ", "separable verb prefix"],
+ ["UC", "UC ", "(idiosyncratic) unit component"],
+ ["VO", "VO ", "vocative"]
+ ];
+
+
+
+// for (var i in negraNodesArray) {
+// ah["corenlp/c=" + negraNodesArray[i][0] + '-'] = negraEdgesArray;
+// };
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/dereko.js b/full/src/main/resources/annotation-scripts/foundries/dereko.js
new file mode 100644
index 0000000..e31f42e
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/dereko.js
@@ -0,0 +1,9 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["DeReKo", "dereko/", "Structure"]
+ );
+
+ ah["dereko/"] = [
+ ["Structure", "s="]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/drukola.js b/full/src/main/resources/annotation-scripts/foundries/drukola.js
new file mode 100644
index 0000000..b57f8a4
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/drukola.js
@@ -0,0 +1,104 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["DRuKoLa", "drukola/", "Lemma, Morphology, Part-of-Speech"]
+ );
+
+ ah["drukola/"] = [
+ ["Lemma", "l="],
+ ["Morphology", "m="],
+ ["Part-of-Speech", "p="]
+ ];
+
+ ah["drukola/m="] = [
+ ["CTAG", "ctag:"]
+ ];
+
+ ah["drukola/m=ctag:"] = [
+ ["A","a ","Adjective"],
+ ["Y","y ","Abbreviation"],
+ ["AN","an ","Adjective, Indefinite"],
+ ["APRY","apry ","Adjective, Plural, Direct, Definite"],
+ ["APN","apn ","Adjective, Plural, Indefinite"],
+ ["APOY","apoy ","Adjective, Plural, Oblique, Definite"],
+ ["APON","apon ","Adjective, Plural, Oblique, Indefinite"],
+ ["ASRY","asry ","Adjective, Singular, Direct, Definite"],
+ ["ASN","asn ","Adjective, Singular, Indefinite"],
+ ["ASOY","asoy ","Adjective, Singular, Oblique, Definite"],
+ ["ASON","ason ","Adjective, Singular, Oblique, Indefinite"],
+ ["ASVY","asvy ","Adjective, Singular, Vocative, Definite"],
+ ["ASVN","asvn ","Adjective, Singular, Vocative, Indefinite"],
+ ["R","r ","Adverb"],
+ ["RC","rc ","Adverb, Portmanteau"],
+ ["TS","ts ","Article, Definite or Possessive, Singular"],
+ ["TP","tp ","Article, Indefinite or Possessive, Plural"],
+ ["TPR","tpr ","Article, non-Possessive, Plural, Direct"],
+ ["TPO","tpo ","Article, non-Possessive, Plural, Oblique"],
+ ["TSR","tsr ","Article, non-Possessive, Singular, Direct"],
+ ["TSO","tso ","Article, non-Possessive, Singular, Oblique"],
+ ["NPRY","npry ","Common Noun, Plural, Direct, Definite"],
+ ["NPN","npn ","Common Noun, Plural, Indefinite"],
+ ["NPOY","npoy ","Common Noun, Plural, Oblique, Definite"],
+ ["NPVY","npvy ","Common Noun, Plural, Vocative, Definite"],
+ ["NN","nn ","Common Noun, singular"],
+ ["NSY","nsy ","Common Noun, Singular, Definite"],
+ ["NSRY","nsry ","Common Noun, Singular, Direct, Definite"],
+ ["NSRN","nsrn ","Common Noun, Singular, Direct, Indefinite"],
+ ["NSN","nsn ","Common Noun, Singular, Indefinite"],
+ ["NSOY","nsoy ","Common Noun, Singular, Oblique, Definite"],
+ ["NSON","nson ","Common Noun, Singular, Oblique, Indefinite"],
+ ["NSVY","nsvy ","Common Noun, Singular, Vocative, Definite"],
+ ["NSVN","nsvn ","Common Noun, Singular, Vocative, Indefinite"],
+ ["CR","cr ","Conjunctio, portmanteau"],
+ ["C","c ","Conjunction"],
+ ["QF","qf ","Future Particle"],
+ ["QN","qn ","Infinitival Particle"],
+ ["I","i ","Interjection"],
+ ["QZ","qz ","Negative Particle"],
+ ["M","m ","Numeral"],
+ ["PP","pp ","Personal Pronoun"],
+ ["PPP","ppp ","Personal Pronoun, Plural"],
+ ["PPPA","pppa ","Personal Pronoun, Plural, Acc."],
+ ["PPPD","pppd ","Personal Pronoun, Plural, Dative"],
+ ["PPPR","pppr ","Personal Pronoun, Plural, Direct"],
+ ["PPPO","pppo ","Personal Pronoun, Plural, Oblique"],
+ ["PPS","pps ","Personal Pronoun, Singular"],
+ ["PPSA","ppsa ","Personal Pronoun, Singular, Accusative"],
+ ["PPSD","ppsd ","Personal Pronoun, Singular, Dative"],
+ ["PPSR","ppsr ","Personal Pronoun, Singular, Direct"],
+ ["PPSN","ppsn ","Personal Pronoun, Singular, Nominative"],
+ ["PPSO","ppso ","Personal Pronoun, Singular, Oblique"],
+ ["S","s ","Preposition"],
+ ["DMPR","dmpr ","Pronoun or Determiner, Demonstrative, Plural, Direct"],
+ ["DMPO","dmpo ","Pronoun or Determiner, Demonstrative, Plural, Oblique"],
+ ["DMSR","dmsr ","Pronoun or Determiner, Demonstrative, Singular, Direct"],
+ ["DMSO","dmso ","Pronoun or Determiner, Demonstrative, Singular, Oblique"],
+ ["PS","ps ","Pronoun or Determiner, Poss or Emph"],
+ ["PSS","pss ","Pronoun or Determiner, Poss or Emph, Singular"],
+ ["RELR","relr ","Pronoun or Determiner, Relative, Direct"],
+ ["RELO","relo ","Pronoun or Determiner, Relative, Oblique"],
+ ["NP","np ","Proper Noun"],
+ ["PI","pi ","Quantifier Pronoun or Determiner"],
+ ["PXA","pxa ","Reflexive Pronoun, Accusative"],
+ ["PXD","pxd ","Reflexive Pronoun, Dative"],
+ ["X","x ","Residual"],
+ ["QS","qs ","Subjunctive Particle"],
+ ["VA","va ","Verb, Auxiliary"],
+ ["VA1","va1 ","Verb, Auxiliary, 1st person"],
+ ["VA2P","va2p ","Verb, Auxiliary, 2nd person, Plural"],
+ ["VA2S","va2s ","Verb, Auxiliary, 2nd person, Singular"],
+ ["VA3","va3 ","Verb, Auxiliary, 3rd person"],
+ ["VA3P","va3p ","Verb, Auxiliary, 3rd person, Plural"],
+ ["VA3S","va3s ","Verb, Auxiliary, 3rd person, Singular"],
+ ["VA1P","va1p ","Verb, Auxiliary,1st person, Plural"],
+ ["VA1S","va1s ","Verb, Auxiliary,1st person, Singular"],
+ ["VG","vg ","Verb, Gerund"],
+ ["VN","vn ","Verb, Infinitive"],
+ ["V1","v1 ","Verb, Main, 1st person"],
+ ["V2","v2 ","Verb, Main, 2nd person"],
+ ["V3","v3 ","Verb, Main, 3rd person"],
+ ["VPPF","vppf ","Verb, Participle, Plural, Feminine"],
+ ["VPPM","vppm ","Verb, Participle, Plural, Masculine"],
+ ["VPSF","vpsf ","Verb, Participle, Singular, Feminine"],
+ ["VPSM","vpsm ","Verb, Participle, Singular, Masculine"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/lwc.js b/full/src/main/resources/annotation-scripts/foundries/lwc.js
new file mode 100644
index 0000000..600bc57
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/lwc.js
@@ -0,0 +1,57 @@
+define(["hint/foundries","hint/foundries/negraedges"], function (ah, negraEdgesArray) {
+ ah["-"].push(
+ ["LWC", "lwc/", "Dependency"]
+ );
+
+ ah["lwc/"] = [
+ ["Dependency", "d="]
+ ];
+
+ ah["lwc/d="] = [
+ ["AC","AC ","adpositional case marker"],
+ ["ADC","ADC ","adjective component"],
+ ["AMS","AMS ","measure argument of adj"],
+ ["APP","APP ","apposition"],
+ ["AVC","AVC ","adverbial phrase component"],
+ ["CC","CC ","comparative complement"],
+ ["CD","CD ","coordinating conjunction"],
+ ["CJ","CJ ","conjunct"],
+ ["CM","CM ","comparative concjunction"],
+ ["CP","CP ","complementizer"],
+ ["DA","DA ","dative"],
+ ["DH","DH ","discourse-level head"],
+ ["DM","DM ","discourse marker"],
+ ["GL","GL ","prenominal genitive"],
+ ["GR","GR ","postnominal genitive"],
+ ["HD","HD ","head"],
+ ["JU","JU ","junctor"],
+ ["MC","MC ","comitative"],
+ ["MI","MI ","instrumental"],
+ ["ML","ML ","locative"],
+ ["MNR","MNR ","postnominal modifier"],
+ ["MO","MO ","modifier"],
+ ["MR","MR ","rhetorical modifier"],
+ ["MW","MW ","way (directional modifier)"],
+ ["NG","NG ","negation"],
+ ["NK","NK ","noun kernel modifier"],
+ ["NMC","NMC ","numerical component"],
+ ["OA","OA ","accusative object"],
+ ["OA2","OA2 ","second accusative object"],
+ ["OC","OC ","clausal object"],
+ ["OG","OG ","genitive object"],
+ ["PD","PD ","predicate"],
+ ["PG","PG ","pseudo-genitive"],
+ ["PH","PH ","placeholder"],
+ ["PM","PM ","morphological particle"],
+ ["PNC","PNC ","proper noun component"],
+ ["RC","RC ","relative clause"],
+ ["RE","RE ","repeated element"],
+ ["RS","RS ","reported speech"],
+ ["SB","SB ","subject"],
+ ["SBP","SBP ","passivised subject (PP)"],
+ ["SP","SP ","subject or predicate"],
+ ["SVP","SVP ","separable verb prefix"],
+ ["UC","UC ","(idiosyncratic) unit component"],
+ ["VO","VO ","vocative"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/malt.js b/full/src/main/resources/annotation-scripts/foundries/malt.js
new file mode 100644
index 0000000..c8c837b
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/malt.js
@@ -0,0 +1,47 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["Malt", "malt/", "Dependency"]
+ );
+
+ ah["malt/"] = [
+ ["Dependency", "d="]
+ ];
+
+ ah["malt/d="] = [
+ ["-PUNCT-", "-PUNCT- ",""],
+ ["-UNKNOWN-","-UNKNOWN- ",""],
+ ["ADV","ADV ",""],
+ ["APP","APP ",""],
+ ["ATTR","ATTR ",""],
+ ["AUX","AUX ",""],
+ ["AVZ","AVZ ",""],
+ ["CJ","CJ ",""],
+ ["DET","DET ",""],
+ ["EXPL","EXPL ",""],
+ ["GMOD","GMOD ",""],
+ ["GRAD","GRAD ",""],
+ ["KOM","KOM ",""],
+ ["KON","KON ",""],
+ ["KONJ","KONJ ",""],
+ ["NEB","NEB ",""],
+ ["OBJA","OBJA ",""],
+ ["OBJC","OBJC ",""],
+ ["OBJD","OBJD ",""],
+ ["OBJG","OBJG ",""],
+ ["OBJI","OBJI ",""],
+ ["OBJP","OBJP ",""],
+ ["PAR","PAR ",""],
+ ["PART","PART ",""],
+ ["PN","PN ",""],
+ ["PP","PP ",""],
+ ["PRED","PRED ",""],
+ ["REL","REL ",""],
+ ["ROOT","ROOT ",""],
+ ["S","S ",""],
+ ["SUBJ","SUBJ ",""],
+ ["SUBJC","SUBJC ",""],
+ ["ZEIT","ZEIT ",""],
+ ["gmod-app","gmod-app ",""],
+ ["koord","koord ",""]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/marmot.js b/full/src/main/resources/annotation-scripts/foundries/marmot.js
new file mode 100644
index 0000000..a6e5b61
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/marmot.js
@@ -0,0 +1,120 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+ ah["-"].push(
+ ["MarMoT", "marmot/", "Morphology, Part-of-Speech"]
+ );
+
+ ah["marmot/"] = [
+ ["Morphology", "m="],
+ ["Part-of-Speech", "p="]
+ ];
+
+ ah["marmot/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"]
+ ];
+
+ ah["marmot/m="] = [
+ ["Case", "case:"],
+ ["Degree", "degree:"],
+ ["Gender", "gender:"],
+ ["Mood", "mood:"],
+ ["Number", "number:"],
+ ["Person", "person:"],
+ ["Tense","tense:"],
+ ["No type", "<no-type> "]
+ ];
+
+ ah["marmot/m=case:"] = [
+ ["acc", "acc ", "Accusative"],
+ ["dat","dat ", "Dative"],
+ ["gen", "gen ","Genitive"],
+ ["nom","nom ", "Nominative"],
+ ["*","* ", "Undefined"]
+ ];
+
+ ah["marmot/m=degree:"] = [
+ ["comp","comp ", "Comparative"],
+ ["pos","pos ", "Positive"],
+ ["sup","sup ", "Superative"]
+ ];
+
+ ah["marmot/m=gender:"] = [
+ ["fem", "fem ", "Feminium"],
+ ["masc", "masc ", "Masculinum"],
+ ["neut","neut ", "Neuter"],
+ ["*","* ","Undefined"]
+ ];
+
+ ah["marmot/m=mood:"] = [
+ ["imp","imp ", "Imperative"],
+ ["ind","ind ", "Indicative"],
+ ["subj","subj ", "Subjunctive"]
+ ];
+
+ ah["marmot/m=number:"] = [
+ ["pl","pl ","Plural"],
+ ["sg","sg ","Singular"],
+ ["*","* ","Undefined"]
+ ];
+
+ ah["marmot/m=person:"] = [
+ ["1","1 ", "First Person"],
+ ["2","2 ", "Second Person"],
+ ["3","3 ", "Third Person"]
+ ];
+
+ ah["marmot/m=tense:"] = [
+ ["past","past ", "Past"],
+ ["pres","pres ", "Present"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/mate.js b/full/src/main/resources/annotation-scripts/foundries/mate.js
new file mode 100644
index 0000000..f0d5c12
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/mate.js
@@ -0,0 +1,130 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+// var mateSttsArray = sttsArray.slice(0);
+// mateSttsArray.push(
+// ["<root-POS>","<root-POS>","Root Part of Speech"]
+// );
+
+ var ah = KorAP.annotationHelper = KorAP.annotationHelper || { "-" : [] };
+
+ ah["-"].push(
+ ["Mate", "mate/", "Lemma, Morphology, Part-of-Speech"]
+ );
+
+ ah["mate/"] = [
+ // Inactive: "d" : ["d=", "Dependency"],
+ ["Lemma", "l="],
+ ["Morphology", "m="],
+ ["Part-of-Speech", "p="]
+ ];
+
+ // Inactive: mate/d=
+ ah["mate/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"],
+ ["<root-POS>","<root-POS>","Root Part of Speech"]
+ ];
+
+ ah["mate/m="] = [
+ ["Case", "case:"],
+ ["Degree", "degree:"],
+ ["Gender", "gender:"],
+ ["Mood", "mood:"],
+ ["Number", "number:"],
+ ["Person", "person:"],
+ ["Tense","tense:"],
+ ["No type", "<no-type> "]
+ ];
+
+ ah["mate/m=case:"] = [
+ ["acc", "acc ", "Accusative"],
+ ["dat","dat ", "Dative"],
+ ["gen", "gen ","Genitive"],
+ ["nom","nom ", "Nominative"],
+ ["*","* ", "Undefined"]
+ ];
+
+ ah["mate/m=degree:"] = [
+ ["comp","comp ", "Comparative"],
+ ["pos","pos ", "Positive"],
+ ["sup","sup ", "Superative"]
+ ];
+
+ ah["mate/m=gender:"] = [
+ ["fem", "fem ", "Feminium"],
+ ["masc", "masc ", "Masculinum"],
+ ["neut","neut ", "Neuter"],
+ ["*","* ","Undefined"]
+ ];
+
+ ah["mate/m=mood:"] = [
+ ["imp","imp ", "Imperative"],
+ ["ind","ind ", "Indicative"],
+ ["subj","subj ", "Subjunctive"]
+ ];
+
+ ah["mate/m=number:"] = [
+ ["pl","pl ","Plural"],
+ ["sg","sg ","Singular"],
+ ["*","* ","Undefined"]
+ ];
+
+ ah["mate/m=person:"] = [
+ ["1","1 ", "First Person"],
+ ["2","2 ", "Second Person"],
+ ["3","3 ", "Third Person"]
+ ];
+ ah["mate/m=tense:"] = [
+ ["past","past ", "Past"],
+ ["pres","pres ", "Present"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/opennlp.js b/full/src/main/resources/annotation-scripts/foundries/opennlp.js
new file mode 100644
index 0000000..d790eba
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/opennlp.js
@@ -0,0 +1,65 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+ ah["-"].push(
+ ["OpenNLP", "opennlp/", "Part-of-Speech"]
+ );
+
+ ah["opennlp/"] = [
+ ["Part-of-Speech", "p="]
+ ];
+
+// ah["opennlp/p="] = sttsArray;
+ ah["opennlp/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js b/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js
new file mode 100644
index 0000000..850617f
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/schreibgebrauch.js
@@ -0,0 +1,117 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+// var sgbrSttsArray = sttsArray.slice(0);
+
+ // Push specific information for Schreibgebrauch
+// sgbrSttsArray.push(
+// ["NNE", "NNE", "Normal Nomina with Named Entity"],
+// ["ADVART","ADVART", "Adverb with Article"],
+// ["EMOASC","EMOASC", "ASCII emoticon"],
+// ["EMOIMG","EMOIMG", "Graphic emoticon"],
+// ["ERRTOK","ERRTOK", "Tokenisation Error"],
+// ["HST", "HST", "Hashtag"],
+// ["KOUSPPER","KOUSPPER", "Subordinating Conjunction (with Sentence) with Personal Pronoun"],
+// ["ONO", "ONO", "Onomatopoeia"],
+// ["PPERPPER","PPERPPER", "Personal Pronoun with Personal Pronoun"],
+// ["URL", "URL", "Uniform Resource Locator"],
+// ["VAPPER", "VAPPER", "Finite Auxiliary Verb with Personal Pronoun"],
+// ["VMPPER", "VMPPER", "Fintite Modal Verb with Personal Pronoun"],
+// ["VVPPER", "VVPPER", "Finite Full Verb with Personal Pronoun"],
+// ["AW", "AW", "Interaction Word"],
+// ["ADR", "ADR", "Addressing Term"],
+// ["AWIND", "AWIND", "Punctuation Indicating Addressing Term"],
+// ["ERRAW","ERRAW", "Part of Erroneously Separated Compound"]
+// /*
+// As KorAP currently doesn't support these tags, they could also be ommited
+// ["_KOMMA", "_KOMMA", "Comma"],
+// ["_SONST", "_SONST", "Intrasentential Punctuation Mark"],
+// ["_ENDE", "_ENDE", "Punctuation Mark at the end of the Sentence"]
+// */
+// );
+
+ // Sort by tag
+ sgbrSttsArray.sort(function (a,b) { return a[0].localeCompare(b[0]) });
+
+
+ ah["-"].push(
+ ["Schreibgebrauch", "sgbr/", "Lemma, Lemma Variants, Part-of-Speech"]
+ );
+
+ ah["sgbr/"] = [
+ ["Lemma", "l="],
+ ["Lemma Variants", "lv="],
+ ["Part-of-Speech", "p="]
+ ];
+
+// ah["sgbr/p="] = sgbrSttsArray;
+ ah["sgbr/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"],
+ ["NNE", "NNE", "Normal Nomina with Named Entity"],
+ ["ADVART","ADVART", "Adverb with Article"],
+ ["EMOASC","EMOASC", "ASCII emoticon"],
+ ["EMOIMG","EMOIMG", "Graphic emoticon"],
+ ["ERRTOK","ERRTOK", "Tokenisation Error"],
+ ["HST", "HST", "Hashtag"],
+ ["KOUSPPER","KOUSPPER", "Subordinating Conjunction (with Sentence) with Personal Pronoun"],
+ ["ONO", "ONO", "Onomatopoeia"],
+ ["PPERPPER","PPERPPER", "Personal Pronoun with Personal Pronoun"],
+ ["URL", "URL", "Uniform Resource Locator"],
+ ["VAPPER", "VAPPER", "Finite Auxiliary Verb with Personal Pronoun"],
+ ["VMPPER", "VMPPER", "Fintite Modal Verb with Personal Pronoun"],
+ ["VVPPER", "VVPPER", "Finite Full Verb with Personal Pronoun"],
+ ["AW", "AW", "Interaction Word"],
+ ["ADR", "ADR", "Addressing Term"],
+ ["AWIND", "AWIND", "Punctuation Indicating Addressing Term"],
+ ["ERRAW","ERRAW", "Part of Erroneously Separated Compound"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/treetagger.js b/full/src/main/resources/annotation-scripts/foundries/treetagger.js
new file mode 100644
index 0000000..78a692a
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/treetagger.js
@@ -0,0 +1,66 @@
+define(["hint/foundries","hint/foundries/stts"], function (ah, sttsArray) {
+ ah["-"].push(
+ ["TreeTagger", "tt/", "Lemma, Part-of-Speech"]
+ );
+
+ ah["tt/"] = [
+ ["Lemma", "l="],
+ ["Part-of-Speech", "p="]
+ ];
+
+// ah["tt/p="] = sttsArray;
+ ah["tt/p="] = [
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/foundries/xip.js b/full/src/main/resources/annotation-scripts/foundries/xip.js
new file mode 100644
index 0000000..ff7ac5e
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/foundries/xip.js
@@ -0,0 +1,12 @@
+define(["hint/foundries"], function (ah) {
+ ah["-"].push(
+ ["Xerox Parser", "xip/", "Constituency, Lemma, Part-of-Speech"]
+ );
+
+ ah["xip/"] = [
+ ["Constituency", "c="],
+ // Inactive: ["Dependency", "d="],
+ ["Lemma", "l="],
+ ["Part-of-Speech", "p="]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/negraedges.js b/full/src/main/resources/annotation-scripts/variables/negraedges.js
new file mode 100644
index 0000000..7dc9b0f
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/negraedges.js
@@ -0,0 +1,51 @@
+define(function () {
+ // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/negra-corpus.html
+ // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/kanten.html
+ return [
+ ["AC","AC ","adpositional case marker"],
+ ["ADC","ADC ","adjective component"],
+ ["AMS","AMS ","measure argument of adj"],
+ ["APP","APP ","apposition"],
+ ["AVC","AVC ","adverbial phrase component"],
+ ["CC","CC ","comparative complement"],
+ ["CD","CD ","coordinating conjunction"],
+ ["CJ","CJ ","conjunct"],
+ ["CM","CM ","comparative concjunction"],
+ ["CP","CP ","complementizer"],
+ ["DA","DA ","dative"],
+ ["DH","DH ","discourse-level head"],
+ ["DM","DM ","discourse marker"],
+ ["GL","GL ","prenominal genitive"],
+ ["GR","GR ","postnominal genitive"],
+ ["HD","HD ","head"],
+ ["JU","JU ","junctor"],
+ ["MC","MC ","comitative"],
+ ["MI","MI ","instrumental"],
+ ["ML","ML ","locative"],
+ ["MNR","MNR ","postnominal modifier"],
+ ["MO","MO ","modifier"],
+ ["MR","MR ","rhetorical modifier"],
+ ["MW","MW ","way (directional modifier)"],
+ ["NG","NG ","negation"],
+ ["NK","NK ","noun kernel modifier"],
+ ["NMC","NMC ","numerical component"],
+ ["OA","OA ","accusative object"],
+ ["OA2","OA2 ","second accusative object"],
+ ["OC","OC ","clausal object"],
+ ["OG","OG ","genitive object"],
+ ["PD","PD ","predicate"],
+ ["PG","PG ","pseudo-genitive"],
+ ["PH","PH ","placeholder"],
+ ["PM","PM ","morphological particle"],
+ ["PNC","PNC ","proper noun component"],
+ ["RC","RC ","relative clause"],
+ ["RE","RE ","repeated element"],
+ ["RS","RS ","reported speech"],
+ ["SB","SB ","subject"],
+ ["SBP","SBP ","passivised subject (PP)"],
+ ["SP","SP ","subject or predicate"],
+ ["SVP","SVP ","separable verb prefix"],
+ ["UC","UC ","(idiosyncratic) unit component"],
+ ["VO","VO ","vocative"]
+ ];
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/negranodes.js b/full/src/main/resources/annotation-scripts/variables/negranodes.js
new file mode 100644
index 0000000..b6be581
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/negranodes.js
@@ -0,0 +1,32 @@
+define(function () {
+ // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/negra-corpus.html
+ // http://www.coli.uni-saarland.de/projects/sfb378/negra-corpus/knoten.html
+ return [
+ ["AA", "AA", "superlative phrase with 'am'"],
+ ["AP","AP", "adjektive phrase"],
+ ["AVP","AVP", "adverbial phrase"],
+ ["CAP","CAP", "coordinated adjektive phrase"],
+ ["CAVP","CAVP", "coordinated adverbial phrase"],
+ ["CAC","CAC", "coordinated adposition"],
+ ["CCP","CCP", "coordinated complementiser"],
+ ["CH","CH", "chunk"],
+ ["CNP","CNP", "coordinated noun phrase"],
+ ["CO","CO", "coordination"],
+ ["CPP","CPP", "coordinated adpositional phrase"],
+ ["CS","CS", "coordinated sentence"],
+ ["CVP","CVP", "coordinated verb phrase (non-finite)"],
+ ["CVZ","CVZ", "coordinated zu-marked infinitive"],
+ ["DL","DL", "discourse level constituent"],
+ ["ISU","ISU", "idiosyncratis unit"],
+ ["MPN","MPN", "multi-word proper noun"],
+ ["MTA","MTA", "multi-token adjective"],
+ ["NM","NM", "multi-token number"],
+ ["NP","NP", "noun phrase"],
+ ["PP","PP", "adpositional phrase"],
+ ["QL","QL", "quasi-languag"],
+ ["ROOT","ROOT", "root node"],
+ ["S","S", "sentence"],
+ ["VP","VP", "verb phrase (non-finite)"],
+ ["VZ","VZ", "zu-marked infinitive"]
+ ]
+});
diff --git a/full/src/main/resources/annotation-scripts/variables/stts.js b/full/src/main/resources/annotation-scripts/variables/stts.js
new file mode 100644
index 0000000..f7d3b62
--- /dev/null
+++ b/full/src/main/resources/annotation-scripts/variables/stts.js
@@ -0,0 +1,59 @@
+define(function () {
+ return [
+ // http://www.ids-mannheim.de/cosmas2/projekt/referenz/stts/morph.html
+ // http://nachhalt.sfb632.uni-potsdam.de/owl-docu/stts.html
+ // "$.", "$(", "$,"
+ ["ADJA","ADJA ", "Attributive Adjective"],
+ ["ADJD","ADJD ", "Predicative Adjective"],
+ ["ADV","ADV ", "Adverb"],
+ ["APPO","APPO ", "Postposition"],
+ ["APPR","APPR ", "Preposition"],
+ ["APPRART","APPRART ", "Preposition with Determiner"],
+ ["APZR","APZR ","Right Circumposition"],
+ ["ART","ART ", "Determiner"],
+ ["CARD","CARD ", "Cardinal Number"],
+ ["FM","FM ", "Foreign Material"],
+ ["ITJ","ITJ ", "Interjection"],
+ ["KOKOM","KOKOM ", "Comparison Particle"],
+ ["KON","KON ", "Coordinating Conjuncion"],
+ ["KOUI","KOUI ", "Subordinating Conjunction with 'zu'"],
+ ["KOUS","KOUS ", "Subordinating Conjunction with Sentence"],
+ ["NE","NE ", "Named Entity"],
+ ["NN","NN ", "Normal Nomina"],
+ ["PAV", "PAV ", "Pronominal Adverb"],
+ ["PDAT","PDAT ","Attributive Demonstrative Pronoun"],
+ ["PDS","PDS ", "Substitutive Demonstrative Pronoun"],
+ ["PIAT","PIAT ", "Attributive Indefinite Pronoun without Determiner"],
+ ["PIDAT","PIDAT ", "Attributive Indefinite Pronoun with Determiner"],
+ ["PIS","PIS ", "Substitutive Indefinite Pronoun"],
+ ["PPER","PPER ", "Personal Pronoun"],
+ ["PPOSAT","PPOSAT ", "Attributive Possessive Pronoun"],
+ ["PPOSS","PPOSS ", "Substitutive Possessive Pronoun"],
+ ["PRELAT","PRELAT ", "Attributive Relative Pronoun"],
+ ["PRELS","PRELS ", "Substitutive Relative Pronoun"],
+ ["PRF","PRF ", "Reflexive Pronoun"],
+ ["PROAV","PROAV ", "Pronominal Adverb"],
+ ["PTKA","PTKA ","Particle with Adjective"],
+ ["PTKANT","PTKANT ", "Answering Particle"],
+ ["PTKNEG","PTKNEG ", "Negation Particle"],
+ ["PTKVZ","PTKVZ ", "Separated Verbal Particle"],
+ ["PTKZU","PTKZU ", "'zu' Particle"],
+ ["PWAT","PWAT ", "Attributive Interrogative Pronoun"],
+ ["PWAV","PWAV ", "Adverbial Interrogative Pronoun"],
+ ["PWS","PWS ", "Substitutive Interrogative Pronoun"],
+ ["TRUNC","TRUNC ","Truncated"],
+ ["VAFIN","VAFIN ", "Auxiliary Finite Verb"],
+ ["VAIMP","VAIMP ", "Auxiliary Finite Imperative Verb"],
+ ["VAINF","VAINF ", "Auxiliary Infinite Verb"],
+ ["VAPP","VAPP ", "Auxiliary Perfect Participle"],
+ ["VMFIN","VMFIN ", "Modal Finite Verb"],
+ ["VMINF","VMINF ", "Modal Infinite Verb"],
+ ["VMPP","VMPP ", "Modal Perfect Participle"],
+ ["VVFIN","VVFIN ","Finite Verb"],
+ ["VVIMP","VVIMP ", "Finite Imperative Verb"],
+ ["VVINF","VVINF ", "Infinite Verb"],
+ ["VVIZU","VVIZU ", "Infinite Verb with 'zu'"],
+ ["VVPP","VVPP ", "Perfect Participle"],
+ ["XY", "XY ", "Non-Word"]
+ ];
+});
diff --git a/full/src/main/resources/lite-config.xml b/full/src/main/resources/lite-config.xml
index 4b6a074..5c9b8ff 100644
--- a/full/src/main/resources/lite-config.xml
+++ b/full/src/main/resources/lite-config.xml
@@ -132,10 +132,10 @@
<!-- Initialization -->
<!-- <bean id="initializator" class="de.ids_mannheim.de.init.LiteInitializatorImpl"
init-method="init">
- </bean> -->
+ </bean>
<bean id="annotationParser" class="de.ids_mannheim.korap.annotation.AnnotationParser"
scope="singleton" />
-
+ -->
<!-- Search Engine -->
<bean id="search_krill" class="de.ids_mannheim.korap.web.SearchKrill">
<constructor-arg value="${krill.indexDir}" />
diff --git a/full/src/main/resources/service.properties b/full/src/main/resources/service.properties
new file mode 100644
index 0000000..cf2899d
--- /dev/null
+++ b/full/src/main/resources/service.properties
@@ -0,0 +1,3 @@
+kustvakt.version=${project.version}
+krill.version=${krill.version}
+koral.version=${koral.version}
\ No newline at end of file
diff --git a/full/src/main/resources/validation.properties b/full/src/main/resources/validation.properties
new file mode 100644
index 0000000..15e09cd
--- /dev/null
+++ b/full/src/main/resources/validation.properties
@@ -0,0 +1,42 @@
+# The ESAPI validator does many security checks on input, such as canonicalization
+# and whitelist validation. Note that all of these validation rules are applied *after*
+# canonicalization. Double-encoded characters (even with different encodings involved,
+# are never allowed.
+#
+# To use:
+#
+# First set up a pattern below. You can choose any name you want, prefixed by the word
+# "Validation." For example:
+# Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+#
+# Then you can validate in your code against the pattern like this:
+# ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
+# Where maxLength and allowNull are set for you needs, respectively.
+#
+# But note, when you use boolean variants of validation functions, you lose critical
+# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and
+# and use the returned user input which is in canonical form. Consider the following:
+#
+# try {
+# someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
+#
+# Validator.SafeString=^[.;:\\-\\p{Alnum}\\p{Space}]{0,1024}$
+# Validator.password_cap=((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,20})
+Validator.SafeString=^[.;:,=\\*\/\/_()\\-0-9\\p{L}\\p{Space}]{0,1024}$
+Validator.email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
+Validator.ipddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
+Validator.url=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
+# Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
+# Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
+
+# as used by apache commons validator for strings
+# Validator.string=^[\\.;:,\\=&\\*\\/\\/_()\\[\\]@\\|\\-0-9\\p{L}\\p{Space}]{0,1024}$
+
+#Validator.username=^[A-Za-z_.\\d]{6,15}$ by Hanl
+# 21.04.17/FB
+Validator.username=^[A-Za-z_.\\d]{3,20}$
+Validator.password=^((?=.*\\d)(?=.*[A-Za-z])(?!.*[\\(\\)-]).{8,20})$
+
+
+# EM
+Validator.setting=[a-zA-Z0-9_-]+
\ No newline at end of file
diff --git a/full/src/test/resources/network-output/search-result.jsonld b/full/src/test/resources/network-output/search-result.jsonld
new file mode 100644
index 0000000..da55e09
--- /dev/null
+++ b/full/src/test/resources/network-output/search-result.jsonld
@@ -0,0 +1,130 @@
+{
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "meta": {
+ "count": 2,
+ "startIndex": 0,
+ "timeout": 10000,
+ "context": {
+ "left": [
+ "token",
+ 6
+ ],
+ "right": [
+ "token",
+ 6
+ ]
+ },
+ "fields": [
+ "ID",
+ "UID",
+ "textSigle",
+ "corpusID",
+ "author",
+ "title",
+ "subTitle",
+ "textClass",
+ "pubPlace",
+ "pubDate",
+ "availability",
+ "layerInfos",
+ "docSigle",
+ "corpusSigle"
+ ],
+ "version": "0.60.4",
+ "benchmark": "1.503497374 s",
+ "totalResults": 67249248,
+ "serialQuery": "tokens:s:der",
+ "itemsPerPage": 2
+ },
+ "query": {
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "match": "match:eq",
+ "layer": "orth",
+ "key": "der",
+ "foundry": "opennlp",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "src": "Kustvakt",
+ "operation": "operation:injection",
+ "scope": "foundry"
+ }
+ ]
+ }
+ },
+ "collection": {
+ "@type": "koral:doc",
+ "match": "match:eq",
+ "type": "type:regex",
+ "value": "CC-BY.*",
+ "key": "availability",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "src": "Kustvakt",
+ "operation": "operation:insertion",
+ "scope": "availability(FREE)"
+ }
+ ]
+ },
+ "matches": [
+ {
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "meta": {},
+ "hasSnippet": true,
+ "hasTokens": false,
+ "matchID": "match-WUD17/B96/48580-p40-41",
+ "textClass": "staat-gesellschaft biographien-interviews fiktion vermischtes",
+ "textSigle": "WUD17/B96/48580",
+ "author": "Andy king50, u.a.",
+ "docSigle": "WUD17/B96",
+ "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
+ "pubPlace": "URL:http://de.wikipedia.org",
+ "availability": "CC-BY-SA",
+ "title": "Benutzer Diskussion:Brettspieler",
+ "pubDate": "2017-07-01",
+ "corpusSigle": "WUD17",
+ "context": {
+ "left": [
+ "token",
+ 6
+ ],
+ "right": [
+ "token",
+ 6
+ ]
+ },
+ "snippet": "<span class=\"context-left\"><span class=\"more\"><\/span>konstruktiv mitarbeiten kannst, erfährst du auf <\/span><span class=\"match\"><mark>der<\/mark><\/span><span class=\"context-right\"> Seite Starthilfe . Grüße, 19:05, 6. Nov.<span class=\"more\"><\/span><\/span>"
+ },
+ {
+ "@context": "http://korap.ids-mannheim.de/ns/KoralQuery/v0.3/context.jsonld",
+ "meta": {},
+ "hasSnippet": true,
+ "hasTokens": false,
+ "matchID": "match-WUD17/B96/50115-p40-41",
+ "textClass": "staat-gesellschaft biographien-interviews fiktion vermischtes",
+ "textSigle": "WUD17/B96/50115",
+ "author": "Doc.Heintz, u.a.",
+ "docSigle": "WUD17/B96",
+ "layerInfos": "corenlp/c=spans corenlp/p=tokens corenlp/s=spans dereko/s=spans malt/d=rels marmot/m=tokens marmot/p=tokens opennlp/p=tokens opennlp/s=spans tt/l=tokens tt/p=tokens",
+ "pubPlace": "URL:http://de.wikipedia.org",
+ "availability": "CC-BY-SA",
+ "title": "Benutzer Diskussion:Blkviz",
+ "pubDate": "2017-07-01",
+ "corpusSigle": "WUD17",
+ "context": {
+ "left": [
+ "token",
+ 6
+ ],
+ "right": [
+ "token",
+ 6
+ ]
+ },
+ "snippet": "<span class=\"context-left\"><span class=\"more\"><\/span>konstruktiv mitarbeiten kannst, erfährst du auf <\/span><span class=\"match\"><mark>der<\/mark><\/span><span class=\"context-right\"> Seite Starthilfe . Grüße, 20:11, 7.<span class=\"more\"><\/span><\/span>"
+ }
+ ]
+}
diff --git a/full/src/test/resources/pipe-output/test-pipes.jsonld b/full/src/test/resources/pipe-output/test-pipes.jsonld
new file mode 100644
index 0000000..7fde643
--- /dev/null
+++ b/full/src/test/resources/pipe-output/test-pipes.jsonld
@@ -0,0 +1,25 @@
+{
+ "meta": {
+ "snippets": true,
+ "timeout": 10000
+ },
+ "query": {
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "match": "match:eq",
+ "key": [
+ "der",
+ "das"
+ ],
+ "layer": "orth",
+ "rewrites": [{
+ "@type": "koral:rewrite",
+ "src": "Glemm",
+ "operation": "operation:override",
+ "scope": "key"
+ }]
+ }
+ },
+ "@context": "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld"
+}
diff --git a/full/src/test/resources/pipe-output/with-param.jsonld b/full/src/test/resources/pipe-output/with-param.jsonld
new file mode 100644
index 0000000..08c7fda
--- /dev/null
+++ b/full/src/test/resources/pipe-output/with-param.jsonld
@@ -0,0 +1,29 @@
+{
+ "meta": {
+ "snippets": true,
+ "timeout": 10000
+ },
+ "query": {
+ "@type": "koral:token",
+ "wrap": {
+ "@type": "koral:term",
+ "match": "match:eq",
+ "key": [
+ "der",
+ "das",
+ "die"
+ ],
+ "layer": "orth",
+ "rewrites": [
+ {
+ "@type": "koral:rewrite",
+ "src": "Glemm",
+ "operation": "operation:override",
+ "scope": "key"
+ }
+ ]
+ }
+ },
+ "@context": "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld"
+}
+