package no.uio.ifi.trackfind.frontend.listeners;

import com.vaadin.shared.MouseEventDetails;
import com.vaadin.ui.TextArea;
import no.uio.ifi.trackfind.frontend.data.TreeNode;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Moves attribute or value(s) from Tree to TextArea.
 *
 * @author Dmytro Titov
 */
@SuppressWarnings("PMD.NonStaticInitializer")
public abstract class MoveAttributeValueHandler {

    private static Map<Boolean, String> OPERATORS = new HashMap<Boolean, String>() {{
        put(true, "AND ");
        put(false, "OR ");
    }};

    private static String INVERSION = "NOT ";

    /**
     * Fetches mouse click details and processes single or multiple drop.
     *
     * @param textArea          Target: TextArea to move to.
     * @param mouseEventDetails Vaadin MouseEventDetails (with states of keyboard keys as well).
     * @param selectedItems     Set of items selected (being dropped).
     */
    protected void processDragAndDrop(TextArea textArea, MouseEventDetails mouseEventDetails, Set<TreeNode> selectedItems) {
        boolean logicalOperation = mouseEventDetails == null || !mouseEventDetails.isAltKey();
        boolean inversion = mouseEventDetails != null && mouseEventDetails.isShiftKey();
        int size = CollectionUtils.size(selectedItems);
        if (size == 1) {
            processDragAndDropSingle(textArea, logicalOperation, inversion, selectedItems.iterator().next());
        } else if (size > 1) {
            processDragAndDropMultiple(textArea, logicalOperation, inversion, selectedItems);
        }
    }

    /**
     * Processes drop of multiple items.
     *
     * @param textArea         Target: TextArea to move to.
     * @param logicalOperation true for AND, false for OR.
     * @param inversion        true for NOT
     * @param items            Items to drop (always values).
     */
    private void processDragAndDropMultiple(TextArea textArea, boolean logicalOperation, boolean inversion, Set<TreeNode> items) {
        String operator = OPERATORS.get(logicalOperation);
        StringBuilder query = new StringBuilder(textArea.getValue());
        if (StringUtils.isNoneEmpty(query.toString())) {
            query.append(operator);
        }
        if (inversion) {
            query.append(INVERSION);
        }
        TreeNode firstItem = items.iterator().next();
        String path = firstItem.getPath();
        String queryTerm = path.substring(0, path.lastIndexOf(">"));
        String attribute = escapeQueryTerm(queryTerm.split(":")[0]);
        query.append(attribute).append(": (");
        for (TreeNode item : items) {
            query.append(escapeQueryTerm(item.toString())).append(" OR ");
        }
        query = new StringBuilder(query.subSequence(0, query.length() - 4) + ")\n");
        textArea.setValue(query.toString());
    }

    /**
     * Processes drop of single item.
     *
     * @param textArea         Target: TextArea to move to.
     * @param logicalOperation true for AND, false for OR.
     * @param inversion        true for NOT
     * @param item             Item to drop (either attribute or value).
     */
    private void processDragAndDropSingle(TextArea textArea, boolean logicalOperation, boolean inversion, TreeNode item) {
        String operator = OPERATORS.get(logicalOperation);
        String query = textArea.getValue();
        if (StringUtils.isNoneEmpty(query)) {
            query += operator;
        }
        if (inversion) {
            query += INVERSION;
        }
        String path = item.getPath();
        if (item.isValue()) {
            query += escapeQueryTerm(path.substring(0, path.lastIndexOf(">"))) + ": " + escapeQueryTerm(item.toString());
        } else {
            query += escapeQueryTerm(path) + ": ";
        }
        query += "\n";
        textArea.setValue(query);
    }

    /**
     * Sanitizes query for Apache Lucene.
     *
     * @param queryTerm Raw query term (attribute or value).
     * @return Sanitized query term (attribute or value).
     */
    private String escapeQueryTerm(String queryTerm) {
        String escaped = queryTerm.replace("/", "\\/").replace(" ", "\\ ").replace(":", "\\:");
        if (escaped.startsWith("-")) {
            escaped = "\\" + escaped;
        }
        return escaped;
    }

}

@@ -270,16 +270,70 @@
 ngUtils;
+%0Aimport org.apache.solr.client.solrj.util.ClientUtils;
 %0A%0Aimport
@@ -2664,16 +2664,28 @@
 ibute = 
+ClientUtils.
 escapeQu
@@ -2687,20 +2687,21 @@
 apeQuery
-Term
+Chars
 (queryTe
@@ -2829,16 +2829,28 @@
 .append(
+ClientUtils.
 escapeQu
@@ -2844,36 +2844,37 @@
 tils.escapeQuery
-Term
+Chars
 (item.toString()
@@ -3797,32 +3797,44 @@
        query += 
+ClientUtils.
 escapeQueryTerm(
@@ -3824,28 +3824,29 @@
 .escapeQuery
-Term
+Chars
 (path.substr
@@ -3881,24 +3881,36 @@
 )) + %22: %22 + 
+ClientUtils.
 escapeQueryT
@@ -3908,20 +3908,21 @@
 apeQuery
-Term
+Chars
 (item.to
@@ -3970,16 +3970,28 @@
 uery += 
+ClientUtils.
 escapeQu
@@ -3993,20 +3993,21 @@
 apeQuery
-Term
+Chars
 (path) +
@@ -4091,459 +4091,6 @@
  %7D%0A%0A
-    /**%0A     * Sanitizes query for Apache Lucene.%0A     *%0A     * @param queryTerm Raw query term (attribute or value).%0A     * @return Sanitized query term (attribute or value).%0A     */%0A    private String escapeQueryTerm(String queryTerm) %7B%0A        String escaped = queryTerm.replace(%22/%22, %22%5C%5C/%22).replace(%22 %22, %22%5C%5C %22).replace(%22:%22, %22%5C%5C:%22);%0A        if (escaped.startsWith(%22-%22)) %7B%0A            escaped = %22%5C%5C%22 + escaped;%0A        %7D%0A        return escaped;%0A    %7D%0A%0A
 %7D%0A

Use ClientUtils to escape characters in Lucene query