Added comments & updated code structure.
Change-Id: I2ff7adb2954b87e8345a6e0619b14614883c4200
diff --git a/core/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
new file mode 100644
index 0000000..6cacd4b
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/rewrite/CollectionCleanRewrite.java
@@ -0,0 +1,74 @@
+package de.ids_mannheim.korap.rewrite;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+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;
+import de.ids_mannheim.korap.utils.JsonUtils;
+import edu.emory.mathcs.backport.java.util.Arrays;
+
+import java.util.Iterator;
+
+/**
+ * @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/core/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
new file mode 100644
index 0000000..f682414
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/rewrite/FoundryInject.java
@@ -0,0 +1,80 @@
+package de.ids_mannheim.korap.rewrite;
+
+import java.util.Collection;
+
+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.exceptions.KustvaktException;
+import de.ids_mannheim.korap.rewrite.KoralNode.RewriteIdentifier;
+import de.ids_mannheim.korap.user.User;
+import edu.emory.mathcs.backport.java.util.Collections;
+
+/**
+ * @author hanl
+ * @date 30/06/2015
+ */
+public class FoundryInject implements RewriteTask.IterableRewritePath,
+ BeanInjectable {
+
+ private Collection userdaos;
+
+
+ public FoundryInject () {
+ this.userdaos = Collections.emptyList();
+ }
+
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+ User user) throws KustvaktException {
+ LayerMapper mapper;
+ // EM: do not use DB
+// if (user != null && !userdaos.isEmpty()) {
+// UserDataDbIface dao = BeansFactory.getTypeFactory()
+// .getTypeInterfaceBean(userdaos, UserSettings.class);
+// mapper = new LayerMapper(config, dao.get(user));
+// }
+// else
+ mapper = new LayerMapper(config);
+
+ 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");
+ String foundry = mapper.findFoundry(layer);
+ if (foundry != null)
+ node.put("foundry", foundry);
+ }
+ return node;
+ }
+
+
+ @Override
+ public String path () {
+ return "query";
+ }
+
+
+ @Override
+ public JsonNode rewriteResult (KoralNode node) {
+ return null;
+ }
+
+
+ @Override
+ public <T extends ContextHolder> void insertBeans (T beans) {
+ this.userdaos = beans.getUserDataProviders();
+ }
+}
diff --git a/core/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/IdWriter.java
new file mode 100644
index 0000000..1f0dad6
--- /dev/null
+++ b/core/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/core/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/KoralNode.java
new file mode 100644
index 0000000..a8870b7
--- /dev/null
+++ b/core/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/core/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
new file mode 100644
index 0000000..672f7e7
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/rewrite/LayerMapper.java
@@ -0,0 +1,110 @@
+package de.ids_mannheim.korap.rewrite;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.user.Userdata;
+
+/**
+ * @author hanl
+ * @date 14/10/2014
+ */
+public class LayerMapper {
+
+ private Userdata settings;
+ private KustvaktConfiguration config;
+
+
+ public LayerMapper (KustvaktConfiguration config, Userdata settings) {
+ this.settings = settings;
+ this.config = config;
+ }
+
+
+ public LayerMapper (KustvaktConfiguration config) {
+ this.config = config;
+ }
+
+
+ /**
+ * 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) {
+ if (settings != null) {
+ switch (translateLayer(layer.toLowerCase().trim())) {
+ case "d":
+ return (String) settings
+ .get(Attributes.DEFAULT_REL_FOUNDRY);
+ case "c":
+ return (String) settings
+ .get(Attributes.DEFAULT_CONST_FOUNDRY);
+ case "pos":
+ return (String) settings
+ .get(Attributes.DEFAULT_POS_FOUNDRY);
+ case "lemma":
+ return (String) settings
+ .get(Attributes.DEFAULT_LEMMA_FOUNDRY);
+ case "surface":
+ return "opennlp";
+ 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_token();
+ // refers to "structure" and is used for paragraphs or sentence boundaries
+ case "s":
+ return "base";
+ 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 "msd";
+ //todo the orth layer does not need a foundry entry
+ case "orth":
+ return "surface";
+ case "t":
+ return "surface";
+ case "const":
+ return "c";
+ case "p":
+ return "pos";
+ case "l":
+ return "lemma";
+ default:
+ return layer;
+ }
+ }
+
+}
diff --git a/core/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/MetaConstraint.java
new file mode 100644
index 0000000..a9380b9
--- /dev/null
+++ b/core/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/core/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
new file mode 100644
index 0000000..ddd7538
--- /dev/null
+++ b/core/src/main/java/de/ids_mannheim/korap/rewrite/RewriteHandler.java
@@ -0,0 +1,308 @@
+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.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.BeanInjectable;
+import de.ids_mannheim.korap.config.ContextHolder;
+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;
+ private ContextHolder beans;
+
+
+ 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<>();
+ this.beans = null;
+ }
+
+ 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!
+ */
+ 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();
+
+ }
+ }
+
+
+ }
+
+ public void defaultRewriteConstraints () {
+ this.add(FoundryInject.class);
+ }
+}
diff --git a/core/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/RewriteTask.java
new file mode 100644
index 0000000..79065a5
--- /dev/null
+++ b/core/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/core/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java b/core/src/main/java/de/ids_mannheim/korap/rewrite/TreeConstraint.java
new file mode 100644
index 0000000..1315c7a
--- /dev/null
+++ b/core/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;
+ }
+}