annis.gui.flatquerybuilder.FlatQueryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for annis.gui.flatquerybuilder.FlatQueryBuilder.java

Source

/*
 * Copyright 2013 Corpuslinguistic working group Humboldt University Berlin.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package annis.gui.flatquerybuilder;

import annis.gui.QueryController;
import annis.gui.objects.Query;
import annis.libgui.Helper;
import annis.service.objects.AnnisAttribute;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.GenericType;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.vaadin.data.Property;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.NativeSelect;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Panel;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ChameleonTheme;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * @author martin klotz (martin.klotz@hu-berlin.de)
 * @author tom ruette (tom.ruette@hu-berlin.de)
 */
public class FlatQueryBuilder extends Panel implements Button.ClickListener, Property.ValueChangeListener {
    private static final Logger log = LoggerFactory.getLogger(FlatQueryBuilder.class);

    private Button btGo;
    private Button btClear;
    private Button btInverse;
    private Button btInitSpan;
    private Button btInitMeta;
    private Button btInitLanguage;

    private MenuBar addMenu;
    private MenuBar addMenuSpan;
    private MenuBar addMenuMeta;

    private QueryController cp;
    private HorizontalLayout language;
    private HorizontalLayout languagenodes;
    private HorizontalLayout span;
    private HorizontalLayout meta;
    private HorizontalLayout toolbar;
    private HorizontalLayout advanced;
    private VerticalLayout mainLayout;
    private NativeSelect filtering;
    private Collection<VerticalNode> vnodes;
    private Collection<EdgeBox> eboxes;
    private Collection<MetaBox> mboxes;
    private SpanBox spbox;
    private String query;
    private MenuBar.MenuItem spanMenu;
    private ReducingStringComparator rsc;

    private static final String[] REGEX_CHARACTERS = { "\\", "+", ".", "[", "*", "^", "$", "|", "?", "(", ")" };

    private static final String BUTTON_GO_LABEL = "Create AQL Query";
    private static final String BUTTON_CLEAR_LABEL = "Clear the Query Builder";
    private static final String BUTTON_INV_LABEL = "Refresh Query Builder";
    private static final String NO_CORPORA_WARNING = "No corpora selected, please select " + "at least one corpus.";
    private static final String INCOMPLETE_QUERY_WARNING = "Query seems to be incomplete.";
    private static final String QUERY_ERROR_WARNING = "An Error occured. Please check your query.";

    private static final String ADD_LING_PARAM = "Add";
    private static final String ADD_SPAN_PARAM = "Add";
    private static final String CHANGE_SPAN_PARAM = "Change";
    private static final String ADD_META_PARAM = "Add";

    private static final String INFO_INIT_LANG = "In this part of the Query Builder, "
            + "blocks of the linguistic query can be constructed from left to right.";
    private static final String INFO_INIT_SPAN = "This part of the Query Builder "
            + "allows you to define a span annotation within which the above query blocks " + "are confined.";
    private static final String INFO_INIT_META = "Here, you can constrain the linguistic "
            + "query by selecting meta levels.";
    private static final String INFO_FILTER = "When searching in the fields, the "
            + "hits are sorted and filtered according to different mechanisms. Please "
            + "choose a filtering mechanism here.";

    private static final String TOOLBAR_CAPTION = "Toolbar";
    private static final String META_CAPTION = "Meta information";
    private static final String SPAN_CAPTION = "Scope";
    private static final String LANG_CAPTION = "Linguistic sequence";
    private static final String ADVANCED_CAPTION = "Advanced settings";

    public FlatQueryBuilder(QueryController cp) {
        setSizeFull();
        launch(cp);

    }

    private void launch(QueryController cp) {
        this.cp = cp;
        rsc = new ReducingStringComparator();
        this.query = "";
        mainLayout = new VerticalLayout();
        // tracking lists for vertical nodes, edgeboxes and metaboxes
        vnodes = new ArrayList<>();
        eboxes = new ArrayList<>();
        mboxes = new ArrayList<>();
        spbox = null;
        // buttons and checks    
        btGo = new Button(BUTTON_GO_LABEL, (Button.ClickListener) this);
        btGo.setStyleName(ChameleonTheme.BUTTON_SMALL);
        btClear = new Button(BUTTON_CLEAR_LABEL, (Button.ClickListener) this);
        btClear.setStyleName(ChameleonTheme.BUTTON_SMALL);
        btInverse = new Button(BUTTON_INV_LABEL, (Button.ClickListener) this);
        btInverse.setStyleName(ChameleonTheme.BUTTON_SMALL);
        btInitLanguage = new Button("Initialize", (Button.ClickListener) this);
        btInitLanguage.setDescription(INFO_INIT_LANG);
        btInitSpan = new Button("Initialize", (Button.ClickListener) this);
        btInitSpan.setDescription(INFO_INIT_SPAN);
        btInitMeta = new Button("Initialize", (Button.ClickListener) this);
        btInitMeta.setDescription(INFO_INIT_META);
        filtering = new NativeSelect("Filtering mechanisms");
        filtering.setDescription(INFO_FILTER);
        ReducingStringComparator rdc = new ReducingStringComparator();
        Set mappings = rdc.getMappings().keySet();
        int i;
        for (i = 0; i < mappings.size(); i++) {
            String mapname = (String) mappings.toArray()[i];
            filtering.addItem(i);
            filtering.setItemCaption(i, mapname);
        }
        filtering.addItem(i + 1);
        filtering.setItemCaption(i + 1, "generic");
        filtering.select(i + 1);
        filtering.setNullSelectionAllowed(false);
        filtering.setImmediate(true);
        // language layout
        language = new HorizontalLayout();
        languagenodes = new HorizontalLayout();
        language.addComponent(languagenodes);
        language.addComponent(btInitLanguage);
        language.setMargin(true);
        language.setCaption(LANG_CAPTION);
        language.addStyleName("linguistics-panel");
        // span layout
        span = new HorizontalLayout();
        span.setSpacing(true);
        span.addComponent(btInitSpan);
        span.setMargin(true);
        span.setCaption(SPAN_CAPTION);
        span.addStyleName("span-panel");
        // meta layout
        meta = new HorizontalLayout();
        meta.setSpacing(true);
        meta.addComponent(btInitMeta);
        meta.setMargin(true);
        meta.setCaption(META_CAPTION);
        meta.addStyleName("meta-panel");
        // toolbar layout
        toolbar = new HorizontalLayout();
        toolbar.setSpacing(true);
        toolbar.addComponent(btGo);
        toolbar.addComponent(btClear);
        toolbar.addComponent(btInverse);
        toolbar.setMargin(true);
        toolbar.setCaption(TOOLBAR_CAPTION);
        toolbar.addStyleName("toolbar-panel");
        // advanced
        advanced = new HorizontalLayout();
        advanced.setSpacing(true);
        advanced.addComponent(filtering);
        advanced.setMargin(true);
        advanced.setCaption(ADVANCED_CAPTION);
        advanced.addStyleName("advanced-panel");
        // put everything on the layout
        mainLayout.setSpacing(true);
        mainLayout.addComponent(language);
        mainLayout.addComponent(span);
        mainLayout.addComponent(meta);
        mainLayout.addComponent(toolbar);
        mainLayout.addComponent(advanced);
        setContent(mainLayout);
        getContent().setWidth("100%");
        getContent().setHeight("-1px");
    }

    @Override
    public void valueChange(Property.ValueChangeEvent event) {

        initialize();
    }

    @Override
    public void attach() {
        super.attach();
        cp.getState().getSelectedCorpora().addValueChangeListener(this);
    }

    @Override
    public void detach() {
        super.detach();
        cp.getState().getSelectedCorpora().removeValueChangeListener(this);
    }

    private void initialize() {
        // try to remove all existing menus
        try {
            language.removeComponent(addMenu);
            span.removeComponent(addMenuSpan);
            meta.removeComponent(addMenuMeta);
        } catch (Exception e) {
            log.error(null, e);
        }

        //init variables:
        final FlatQueryBuilder sq = this;
        Collection<String> annonames = getAvailableAnnotationNames();
        Collection<String> metanames = getAvailableMetaNames();

        //Code from btInitLanguage:    
        addMenu = new MenuBar();
        //addMenu.setDescription(INFO_INIT_LANG);
        addMenu.setAutoOpen(false);
        final MenuBar.MenuItem add = addMenu.addItem(ADD_LING_PARAM, null);
        for (final String annoname : annonames) {
            add.addItem(annoname, new MenuBar.Command() {
                @Override
                public void menuSelected(MenuBar.MenuItem selectedItem) {
                    if (!vnodes.isEmpty()) {
                        EdgeBox eb = new EdgeBox(sq);
                        languagenodes.addComponent(eb);
                        eboxes.add(eb);
                    }
                    VerticalNode vn = new VerticalNode(annoname, sq);
                    languagenodes.addComponent(vn);
                    vnodes.add(vn);
                    addMenu.setAutoOpen(false);
                }
            });
        }
        language.removeComponent(btInitLanguage);
        language.addComponent(addMenu);

        //Code from btInitSpan:    
        addMenuSpan = new MenuBar();
        //addMenuSpan.setDescription(INFO_INIT_SPAN);
        addMenuSpan.setAutoOpen(false);
        final MenuBar.MenuItem addSpan = addMenuSpan.addItem(ADD_SPAN_PARAM, null);
        for (final String annoname : annonames) {
            addSpan.addItem(annoname, new MenuBar.Command() {
                @Override
                public void menuSelected(MenuBar.MenuItem selectedItem) {
                    sq.removeSpanBox();
                    sq.addSpanBox(annoname);
                    addMenuSpan.setAutoOpen(false);
                }
            });
        }
        spanMenu = addSpan;
        span.removeComponent(btInitSpan);
        span.addComponent(addMenuSpan);

        //Code from btInitMeta:    
        addMenuMeta = new MenuBar();
        //addMenuMeta.setDescription(INFO_INIT_META);  
        addMenuMeta.setAutoOpen(false);
        final MenuBar.MenuItem addMeta = addMenuMeta.addItem(ADD_META_PARAM, null);
        int i = 0;
        for (final String annoname : metanames) {
            addMeta.addItem(annoname, new MenuBar.Command() {
                @Override
                public void menuSelected(MenuBar.MenuItem selectedItem) {
                    MetaBox mb = new MetaBox(annoname, sq);
                    meta.addComponent(mb);
                    mboxes.add(mb);
                    addMenuMeta.setAutoOpen(false);
                    //addMeta.removeChild(selectedItem);
                    selectedItem.setVisible(false);
                }
            });
        }
        meta.removeComponent(btInitMeta);
        meta.addComponent(addMenuMeta);
    }

    private String getAQLFragment(SearchBox sb) {
        String result = "";
        String value = null;
        try {
            value = sb.getValue();
        } catch (java.lang.NullPointerException ex) {
            value = null;
        }
        String level = sb.getAttribute();
        if (value == null) {
            result = level;
        }
        if (value != null) {
            if (sb.isRegEx() && !sb.isNegativeSearch()) {
                result = level + "=/" + value.replace("/", "\\x2F") + "/";
            }
            if (sb.isRegEx() && sb.isNegativeSearch()) {
                result = level + "!=/" + value.replace("/", "\\x2F") + "/";
            }
            if (!sb.isRegEx() && sb.isNegativeSearch()) {
                result = level + "!=\"" + value.replace("\"", "\\x22") + "\"";
            }
            if (!sb.isRegEx() && !sb.isNegativeSearch()) {
                result = level + "=\"" + value.replace("\"", "\\x22") + "\"";
            }
        }
        return result;
    }

    private String getMetaQueryFragment(MetaBox mb) {
        Collection<String> values = mb.getValues();
        if (!values.isEmpty()) {
            StringBuilder result = new StringBuilder("\n& meta::" + mb.getMetaDatum() + " = ");
            if (values.size() == 1) {
                result.append("\"" + values.iterator().next().replace("\"", "\\x22") + "\"");
            } else {
                Iterator<String> itValues = values.iterator();
                result.append("/(" + escapeRegexCharacters(itValues.next()) + ")");
                while (itValues.hasNext()) {
                    result.append("|(" + escapeRegexCharacters(itValues.next()) + ")");
                }
                result.append("/");
            }
            return result.toString();
        }
        return "";
    }

    public String escapeRegexCharacters(String tok) {
        if (tok == null) {
            return "";
        }
        if (tok.equals("")) {
            return "";
        }
        String result = tok;
        for (int i = 0; i < REGEX_CHARACTERS.length; i++) {
            result = result.replace(REGEX_CHARACTERS[i], "\\" + REGEX_CHARACTERS[i]);
        }

        return result.replace("/", "\\x2F");
    }

    public String unescape(String s) {
        //first unescape slashes and quotes:

        s = unescapeSlQ(s);

        //unescape regex characters:
        int i = 1;
        while (i < s.length()) {
            char c0 = s.charAt(i - 1);
            char c1 = s.charAt(i);
            for (int j = 0; j < REGEX_CHARACTERS.length; j++) {
                if ((c1 == REGEX_CHARACTERS[j].charAt(0)) & (c0 == '\\')) {
                    s = s.substring(0, i - 1) + s.substring(i);
                    break;
                }
                if (j == REGEX_CHARACTERS.length - 1) {
                    i++;
                }
            }
        }

        return s;
    }

    public String unescapeSlQ(String s) {
        return s.replace("\\x2F", "/").replace("\\x22", "\"");
    }

    private String getAQLQuery() {
        int count = 1;
        StringBuilder ql = new StringBuilder();
        StringBuilder edgeQuery = new StringBuilder();
        StringBuilder sentenceQuery = new StringBuilder();
        Collection<Integer> sentenceVars = new ArrayList<>();
        Iterator<EdgeBox> itEboxes = eboxes.iterator();
        for (VerticalNode v : vnodes) {
            Collection<SearchBox> sboxes = v.getSearchBoxes();
            for (SearchBox s : sboxes) {
                ql.append(" & " + getAQLFragment(s));
            }
            if (sboxes.isEmpty()) {
                //not sure we want to do it this way:
                ql.append("\n& /.*/");
            }
            sentenceVars.add(Integer.valueOf(count));
            for (int i = 1; i < sboxes.size(); i++) {
                String addQuery = "\n& #" + count + "_=_" + "#" + ++count;
                edgeQuery.append(addQuery);
            }
            count++;
            String edgeQueryAdds = (itEboxes.hasNext())
                    ? "\n& #" + (count - 1) + " " + itEboxes.next().getValue() + " #" + count
                    : "";
            edgeQuery.append(edgeQueryAdds);
        }
        String addQuery = "";
        try {
            SpanBox spb = (SpanBox) span.getComponent(1);
            if ((!spb.isRegEx()) && (!spb.getValue().isEmpty())) {
                addQuery = "\n& " + spb.getAttribute() + " = \"" + spb.getValue() + "\"";
            }
            if (spb.isRegEx()) {
                addQuery = "\n& " + spb.getAttribute() + " = /" + spb.getValue().replace("/", "\\x2F") + "/";
            }
            if (spb.getValue().isEmpty()) {
                addQuery = "\n&" + spb.getAttribute();
            }
            ql.append(addQuery);
            for (Integer i : sentenceVars) {
                sentenceQuery.append("\n& #").append(count).append("_i_#").append(i.toString());
            }
        } catch (Exception ex) {
            ex = null;
        }
        StringBuilder metaQuery = new StringBuilder();
        Iterator<MetaBox> itMetaBoxes = mboxes.iterator();
        while (itMetaBoxes.hasNext()) {
            metaQuery.append(getMetaQueryFragment(itMetaBoxes.next()));
        }
        String fullQuery = (ql.toString() + edgeQuery.toString() + sentenceQuery.toString() + metaQuery.toString());
        if (fullQuery.length() < 3) {
            return "";
        }
        fullQuery = fullQuery.substring(3);//deletes leading " & "
        this.query = fullQuery;
        return fullQuery;
    }

    public void updateQuery() {
        try {
            cp.setQuery(new Query(getAQLQuery(), cp.getState().getSelectedCorpora().getValue()));
        } catch (java.lang.NullPointerException ex) {
            Notification.show(INCOMPLETE_QUERY_WARNING);
        }
    }

    @Override
    public void buttonClick(Button.ClickEvent event) {
        if (cp.getState().getSelectedCorpora().getValue().isEmpty()) {
            Notification.show(NO_CORPORA_WARNING);
        } else {
            if (event.getButton() == btGo) {
                updateQuery();
            }

            if (event.getButton() == btClear) {
                clear();
                updateQuery();
                launch(cp);
            }
            if (event.getComponent() == btInverse) {
                try {
                    loadQuery();
                } catch (EmptyReferenceException e) {
                    log.error(null, e);
                } catch (EqualityConstraintException e) {
                    log.error(null, e);
                } catch (InvalidCharacterSequenceException e) {
                    log.error(null, e);
                } catch (MultipleAssignmentException e) {
                    log.error(null, e);
                } catch (UnknownLevelException e) {
                    log.error(null, e);
                }
            }
            if (event.getComponent() == btInitMeta || event.getComponent() == btInitSpan
                    || event.getComponent() == btInitLanguage) {
                initialize();
            }
        }
    }

    public void removeVerticalNode(VerticalNode v) {
        Iterator<VerticalNode> itVnodes = vnodes.iterator();
        Iterator<EdgeBox> itEboxes = eboxes.iterator();
        VerticalNode vn = itVnodes.next();
        EdgeBox eb = null;

        while (!vn.equals(v)) {
            vn = itVnodes.next();
            eb = itEboxes.next();
        }

        if ((eb == null) & (itEboxes.hasNext())) {
            eb = itEboxes.next();
        }

        vnodes.remove(v);
        if (eb != null) {
            eboxes.remove(eb);
            languagenodes.removeComponent(eb);
        }
        languagenodes.removeComponent(v);
    }

    public void addSpanBox(String level) {
        spbox = new SpanBox(level, this);
        span.addComponent(spbox);
        span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT);
        spanMenu.setText(CHANGE_SPAN_PARAM);
    }

    public void addSpanBox(SpanBox spb) {
        if (spbox == null) {
            spanMenu.setText(CHANGE_SPAN_PARAM);
        } else {
            span.removeComponent(spbox);
        }
        spbox = spb;
        span.addComponent(spbox);
        span.setComponentAlignment(spbox, Alignment.MIDDLE_LEFT);
    }

    public void removeSpanBox() {
        if (spbox != null) {
            span.removeComponent(spbox);
            spbox = null;
            spanMenu.setText(ADD_SPAN_PARAM);
        }
    }

    public void removeSpanBox(SpanBox spb) {
        if (spb.equals(spbox)) {
            removeSpanBox();
        }
    }

    public void removeMetaBox(MetaBox v) {
        meta.removeComponent(v);
        mboxes.remove(v);
        List<MenuBar.MenuItem> items = addMenuMeta.getItems().get(0).getChildren();
        boolean found = false;
        String metalevel = v.getMetaDatum();
        for (int i = 0; (i < items.size()) & !found; i++) {
            MenuBar.MenuItem itm = items.get(i);
            if (itm.getText().equals(metalevel)) {
                itm.setVisible(true);
                found = true;
            }
        }
    }

    public Collection<String> getAnnotationValues(String level) {
        Collection<String> values = new TreeSet<>();
        for (String s : getAvailableAnnotationLevels(level)) {
            values.add(s);
        }
        return values;
    }

    public Set<String> getAvailableAnnotationNames() {
        Set<String> result = new TreeSet<>();
        WebResource service = Helper.getAnnisWebResource();
        // get current corpus selection
        Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue();
        if (service != null) {
            try {
                List<AnnisAttribute> atts = new LinkedList<>();
                for (String corpus : corpusSelection) {
                    atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations")
                            .queryParam("fetchvalues", "false").queryParam("onlymostfrequentvalues", "false")
                            .get(new GenericType<List<AnnisAttribute>>() {
                            }));
                }
                for (AnnisAttribute a : atts) {
                    if (a.getType() == AnnisAttribute.Type.node) {
                        result.add(killNamespace(a.getName()));
                    }
                }
            } catch (ClientHandlerException ex) {
                log.error(null, ex);
            } catch (UniformInterfaceException ex) {
                log.error(null, ex);
            }
        }
        result.add("tok");
        return result;
    }

    public Collection<String> getAvailableAnnotationLevels(String meta) {
        Collection<String> result = new TreeSet<>();
        WebResource service = Helper.getAnnisWebResource();
        // get current corpus selection
        Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue();
        if (service != null) {
            try {
                List<AnnisAttribute> atts = new LinkedList<>();
                for (String corpus : corpusSelection) {
                    atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations")
                            .queryParam("fetchvalues", "true").queryParam("onlymostfrequentvalues", "false")
                            .get(new GenericType<List<AnnisAttribute>>() {
                            }));
                }
                for (AnnisAttribute a : atts) {
                    if (a.getType() == AnnisAttribute.Type.node) {
                        String aa = killNamespace(a.getName());
                        if (aa.equals(meta)) {
                            result.addAll(a.getValueSet());
                        }
                    }
                }
            } catch (ClientHandlerException ex) {
                log.error(null, ex);
            } catch (UniformInterfaceException ex) {
                log.error(null, ex);
            }
        }
        return result;
    }

    public String killNamespace(String qName) {
        String[] splitted = qName.split(":", 2);
        if (splitted.length > 1) {
            return splitted[1];
        } else {
            return qName;
        }
    }

    public Set<String> getAvailableMetaNames() {
        Set<String> result = new TreeSet<>();
        WebResource service = Helper.getAnnisWebResource();
        // get current corpus selection
        Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue();
        if (service != null) {
            try {
                List<AnnisAttribute> atts = new LinkedList<>();

                for (String corpus : corpusSelection) {
                    atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations")
                            .get(new GenericType<List<AnnisAttribute>>() {
                            }));
                }
                for (AnnisAttribute a : atts) {
                    if (a.getType() == AnnisAttribute.Type.meta) {
                        String aa = killNamespace(a.getName());
                        result.add(aa);
                    }
                }
            } catch (ClientHandlerException ex) {
                log.error(null, ex);
            } catch (UniformInterfaceException ex) {
                log.error(null, ex);
            }
        }
        return result;
    }

    public Set<String> getAvailableMetaLevels(String meta) {
        Set<String> result = new TreeSet<>();
        WebResource service = Helper.getAnnisWebResource();
        // get current corpus selection
        Set<String> corpusSelection = cp.getState().getSelectedCorpora().getValue();
        if (service != null) {
            try {
                List<AnnisAttribute> atts = new LinkedList<>();
                for (String corpus : corpusSelection) {
                    atts.addAll(service.path("query").path("corpora").path(corpus).path("annotations")
                            .queryParam("fetchvalues", "true").queryParam("onlymostfrequentvalues", "false")
                            .get(new GenericType<List<AnnisAttribute>>() {
                            }));
                }
                for (AnnisAttribute a : atts) {
                    if (a.getType() == AnnisAttribute.Type.meta) {
                        String aa = killNamespace(a.getName());
                        if (aa.equals(meta)) {
                            result.addAll(a.getValueSet());
                        }
                    }
                }
            } catch (ClientHandlerException ex) {
                log.error(null, ex);
            } catch (UniformInterfaceException ex) {
                log.error(null, ex);
            }
        }
        return result;
    }

    public String getFilterMechanism() {
        return filtering.getItemCaption(filtering.getValue());
    }

    private void clear()
    //check whether it is necessary to do this in a method
    {
        language.removeAllComponents();
        span.removeAllComponents();
        meta.removeAllComponents();
        toolbar.removeAllComponents();
        mainLayout.removeComponent(language);
        mainLayout.removeComponent(span);
        mainLayout.removeComponent(meta);
        mainLayout.removeComponent(toolbar);
        vnodes.clear();
        eboxes.clear();
        mboxes.clear();
    }

    private Collection<String> splitMultipleValueExpression(String expression)
    /*
     * only for complex regex expressions
     */
    {
        ArrayList<String> values = new ArrayList<>();
        String s = expression;

        while (s.length() > 0) {
            if (s.charAt(0) == '|') {
                s = s.substring(1);
            }
            if (s.charAt(0) != '(') {
                values = new ArrayList<>();
                values.add(expression);
                return values;
            } else {
                int pc = 1;
                int i = 1;
                while (pc != 0) {
                    char c = s.charAt(i);
                    if (c == ')') {
                        pc--;
                    } else if (c == '(') {
                        pc++;
                    }
                    i++;
                }
                values.add(unescapeSlQ(s.substring(1, i - 1))); //in respect to removal of parentheses
                s = s.substring(i);
            }
        }

        return values;
    }

    public ReducingStringComparator getRSC() {
        return rsc;
    }

    public void loadQuery() throws UnknownLevelException, EqualityConstraintException, MultipleAssignmentException,
            InvalidCharacterSequenceException, EmptyReferenceException
    /*
     * this method is called by btInverse
     * When the query has changed in the
     * textfield, the query represented by 
     * the query builder is not equal to
     * the one delivered by the text field
     */
    {
        /*get clean query from control panel text field*/
        String tq = cp.getState().getAql().getValue().replace("\n", " ").replace("\r", "");
        //TODO VALIDATE QUERY: (NOT SUFFICIENT YET)
        boolean valid = (!tq.equals(""));
        if (!(query.equals(tq)) & valid) {
            //PROBLEM: LINE BREAKS (simple without anything else)
            try {

                //the dot marks the end of the string - it is not read, it's just a place holder
                tq += ".";
                HashMap<Integer, Constraint> constraints = new HashMap<>();
                ArrayList<Relation> pRelations = new ArrayList<>();
                ArrayList<Relation> eRelations = new ArrayList<>();
                Relation inclusion = null;
                Constraint conInclusion = null;
                ArrayList<Constraint> metaConstraints = new ArrayList<>();

                //parse typed-in Query
                //Step 1: get indices of tq-chars, where constraints are separated (&)
                String tempCon = "";
                int count = 1;
                int maxId = 0;
                boolean inclusionCheck = false;

                for (int i = 0; i < tq.length(); i++) {
                    //improve this Algorithm (compare to constraint)
                    char c = tq.charAt(i);
                    if ((c != '&') & (i != tq.length() - 1)) {
                        if (!((tempCon.length() == 0) & (c == ' '))) //avoids, that a constraint starts with a space
                        {
                            tempCon += c;
                        }
                    } else {
                        while (tempCon.charAt(tempCon.length() - 1) == ' ') {
                            tempCon = tempCon.substring(0, tempCon.length() - 1);
                        }

                        if (tempCon.startsWith("meta::")) {
                            metaConstraints.add(new Constraint(tempCon));
                        } else if (tempCon.startsWith("#")) {
                            Relation r = new Relation(tempCon);
                            if (r.getType() == RelationType.EQUALITY) {
                                eRelations.add(r);
                            } else if (r.getType() == RelationType.PRECEDENCE) {
                                pRelations.add(r);
                            } else if ((r.getType() == RelationType.INCLUSION) & (!inclusionCheck)) {
                                inclusion = r;
                                if (constraints.containsKey(r.getFirst())) {
                                    conInclusion = constraints.get(r.getFirst());
                                    constraints.remove(r.getFirst());
                                }
                                inclusionCheck = true;
                            }
                            int newMax = (r.getFirst() > r.getSecond()) ? r.getFirst() : r.getSecond();
                            maxId = (maxId < newMax) ? newMax : maxId;
                        } else {
                            constraints.put(count++, new Constraint(tempCon));
                        }

                        tempCon = "";
                    }
                }
                /*CHECK FOR EMPTY REFERENCE*/
                /*IDEA: If the highest element-id is not empty, all lower ids can't be empty*/
                /*one additional increment of count has to be taken into account*/
                /*empty means, the element the id is refering to does not exist*/
                if (maxId >= count) {
                    throw new EmptyReferenceException(Integer.toString(maxId));
                }

                /*CHECK FOR INVALID OR REDUNDANT MUTLIPLE VALUE ASSIGNMENT*/
                for (Relation rel : eRelations) {
                    Constraint con1 = constraints.get(rel.getFirst());
                    Constraint con2 = constraints.get(rel.getSecond());
                    if (con1.getLevel().equals(con2.getLevel())) {
                        throw new MultipleAssignmentException(con1.toString() + " <-> " + con2.toString());
                    }
                }

                //create Vertical Nodes
                HashMap<Integer, VerticalNode> indexedVnodes = new HashMap<>();
                VerticalNode vn = null;
                Collection<String> annonames = getAvailableAnnotationNames();
                for (int i : constraints.keySet()) {
                    Constraint con = constraints.get(i);
                    if (!annonames.contains(con.getLevel())) {
                        throw new UnknownLevelException(con.getLevel());
                        //is that a good idea? YES
                    }
                    if (!indexedVnodes.containsKey(i)) {
                        vn = new VerticalNode(con.getLevel(), con.getValue(), this, con.isRegEx(),
                                con.isNegative());
                        if (con.isRegEx()) {
                            SearchBox sb = vn.getSearchBoxes().iterator().next();
                            /*CHECK FIRST IF WE REALLY HAVE A MULTIPLE VALUE EXPRESSION*/
                            Collection<String> mvalue = splitMultipleValueExpression(con.getValue());
                            if (mvalue.size() == 1) {
                                sb.setValue(mvalue.iterator().next());
                            } else {
                                sb.setValue(mvalue);
                            }
                        }
                        indexedVnodes.put(i, vn);
                    }

                    for (Relation rel : eRelations) {
                        if (rel.contains(i)) {
                            int b = rel.whosMyFriend(i);
                            if (!indexedVnodes.containsKey(b)) {
                                indexedVnodes.put(b, null);
                                Constraint bcon = constraints.get(b);
                                SearchBox sb = new SearchBox(bcon.getLevel(), this, vn, bcon.isRegEx(),
                                        bcon.isNegative());
                                Collection<String> values = splitMultipleValueExpression(bcon.getValue());
                                if (values.size() > 1) {
                                    sb.setValue(values);
                                } else {
                                    sb.setValue(bcon.getValue());
                                }
                                vn.addSearchBox(sb);
                            }
                        }
                    }

                }

                //clean query builder surface
                for (VerticalNode v : vnodes) {
                    languagenodes.removeComponent(v);
                }
                vnodes.clear();

                for (EdgeBox eb : eboxes) {
                    languagenodes.removeComponent(eb);
                }
                eboxes.clear();

                for (MetaBox mb : mboxes) {
                    meta.removeComponent(mb);
                }
                mboxes.clear();

                //remove SpanBox
                removeSpanBox();

                VerticalNode first = null;
                int smP = (!pRelations.isEmpty()) ? pRelations.iterator().next().getFirst() : 0;
                int smE = (!eRelations.isEmpty()) ? eRelations.iterator().next().getFirst() : 0;
                if ((smP + smE) == 0) {
                    if (!indexedVnodes.isEmpty()) {
                        first = indexedVnodes.values().iterator().next();
                    }
                } else if ((smP != 0) & (smE != 0)) {
                    first = indexedVnodes.get(Math.min(smE, smP));
                } else {
                    //one value is zero
                    first = indexedVnodes.get(smE + smP);
                }

                if (first != null) {
                    vnodes.add(first);
                    languagenodes.addComponent(first);

                    for (Relation rel : pRelations) {
                        EdgeBox eb = new EdgeBox(this);
                        eb.setValue(rel.getOperator());
                        eboxes.add(eb);
                        VerticalNode v = indexedVnodes.get(rel.getSecond());
                        vnodes.add(v);
                        languagenodes.addComponent(eb);
                        languagenodes.addComponent(v);

                    }
                }

                //build SpanBox
                if (inclusion != null) {
                    addSpanBox(new SpanBox(conInclusion.getLevel(), this, conInclusion.isRegEx()));
                    spbox.setValue(conInclusion.getValue());
                }

                //build MetaBoxes
                for (Constraint mc : metaConstraints) {
                    if (mc.isRegEx()) {
                        Collection<String> values = splitMultipleValueExpression(
                                unescape(unescapeSlQ(mc.getValue())));
                        MetaBox mb = new MetaBox(mc.getLevel(), this);
                        mb.setValue(values);
                        mboxes.add(mb);
                        meta.addComponent(mb);
                    } else {
                        MetaBox mb = new MetaBox(mc.getLevel(), this);
                        //for a particular reason (unknown) setValue() with a String parameter
                        //is not accepted by OptionGroup
                        Collection<String> values = new TreeSet<>();
                        values.add(unescapeSlQ(mc.getValue()));
                        mb.setValue(values);
                        mboxes.add(mb);
                        meta.addComponent(mb);
                    }
                }
                query = tq.substring(0, tq.length() - 1);
            } catch (EmptyReferenceException e) {
                Notification.show(e.getMessage());
            } catch (EqualityConstraintException e) {
                Notification.show(e.getMessage());
            } catch (InvalidCharacterSequenceException e) {
                Notification.show(e.getMessage());
            } catch (MultipleAssignmentException e) {
                Notification.show(e.getMessage());
            } catch (UnknownLevelException e) {
                Notification.show(e.getMessage());
            }
        }
    }

    private class Constraint {
        private String level;
        private String value;
        private boolean regEx;
        private boolean negative;

        public Constraint(String s) throws InvalidCharacterSequenceException {
            int e = 0;
            if (s.contains("=")) {
                while (s.charAt(e) != '=') {
                    e++;
                }

                String l;

                if (s.charAt(e - 1) == '!') {
                    l = s.substring(0, e - 1).replace(" ", "");
                    negative = true;
                } else {
                    l = s.substring(0, e).replace(" ", "").replace("meta::", "");
                    negative = false;
                }

                String v = s.substring(e + 1);
                while (v.startsWith(" ")) {
                    v = v.substring(1);
                }
                if (v.startsWith("\"")) {
                    regEx = false;
                } else {
                    regEx = true;
                }
                //remove " or / :
                v = v.substring(1, v.length() - 1);

                level = l;
                value = v;
            } else if (((s.charAt(0) == '\"') && (s.charAt(s.length() - 1) == '\"'))
                    || ((s.charAt(0) == '/') && (s.charAt(s.length() - 1) == '/'))) {
                level = "tok";
                value = s.substring(1, s.length() - 1);
            } else if ((s.contains("\"")) || (s.contains("/"))) {
                throw new InvalidCharacterSequenceException(s);
            } else {
                level = s;
                value = "";
            }
        }

        public String getLevel() {
            return level;
        }

        public String getValue() {
            return value;
        }

        public boolean isRegEx() {
            return regEx;
        }

        public boolean isNegative() {
            return negative;
        }

        @Override
        public String toString() {
            String op = (negative) ? "!=" : "=";
            String val = (regEx) ? "/" + value + "/" : "\"" + value + "\"";
            return level + op + val;
        }
    }

    private class Relation
    /*
     * Problems:
     * if an operator is used, which is not in the EdgeBoxe's list
     * the programm will crash. Right now. I'm gonna fix this.
     * 
     */
    {
        //operands without '#'
        private int o1, o2;
        private String operator;
        private RelationType type;

        public Relation(String in) throws EqualityConstraintException {

            StringBuilder op = new StringBuilder();
            StringBuilder o1str = new StringBuilder();
            StringBuilder o2str = new StringBuilder();

            int i = 1;
            char c = in.charAt(1);
            in = in.replace(" ", "");

            while ((c != '.') & (c != '>') & (c != '_') & (c != '#') & (c != '-') & (c != '$') & (c != '=')) {
                o1str.append(c);
                i++;
                c = in.charAt(i);
            }
            while (c != '#') {
                op.append(c);
                i++;
                c = in.charAt(i);
            }
            i++;
            while ((i < in.length())) {
                c = in.charAt(i);
                o2str.append(c);
                i++;
            }

            operator = op.toString();
            o1 = Integer.parseInt(o1str.toString());
            o2 = Integer.parseInt(o2str.toString());

            if (operator.startsWith(".")) {
                type = RelationType.PRECEDENCE;
            } else if ((operator.equals("=")) || (operator.equals("_=_"))) {
                type = RelationType.EQUALITY;
                if (o1 > o2) {
                    int tmp = o1;
                    o1 = o2;
                    o2 = tmp;
                } else if (o1 == o2) {
                    throw new EqualityConstraintException(in);
                }
            } else if (operator.equals("_i_")) {
                type = RelationType.INCLUSION;
            }
        }

        public RelationType getType() {
            return type;
        }

        public int getFirst() {
            return o1;
        }

        public int getSecond() {
            return o2;
        }

        public String getOperator() {
            return operator;
        }

        public boolean contains(int a) {
            return ((o1 == a) | (o2 == a));
        }

        public int whosMyFriend(int a) {
            if (a == o1) {
                return o2;
            }
            if (a == o2) {
                return o1;
            }
            return 0;
        }
    }

    private enum RelationType {
        PRECEDENCE, DOMINANCE, INCLUSION, EQUALITY
    }

    private abstract class LoadQueryException extends Exception {
        protected String ERROR_MESSAGE;
        protected String critical;

        public LoadQueryException(String s) {
            critical = s;
        }

        @Override
        public String getMessage() {
            return ERROR_MESSAGE + critical;
        }
    }

    private class UnknownLevelException extends LoadQueryException {
        public UnknownLevelException(String s) {
            super(s);
            ERROR_MESSAGE = "Unknown annotation level: ";
        }
    }

    private class MultipleAssignmentException extends LoadQueryException {
        public MultipleAssignmentException(String s) {
            super(s);
            ERROR_MESSAGE = "Invalid or redundant assignment of multiple values:\n\n";
        }
    }

    private class EqualityConstraintException extends LoadQueryException {
        public EqualityConstraintException(String s) {
            super(s);
            ERROR_MESSAGE = "Invalid use of equality operator: ";
        }
    }

    private class InvalidCharacterSequenceException extends LoadQueryException {
        public InvalidCharacterSequenceException(String s) {
            super(s);
            ERROR_MESSAGE = "Invalid character sequence: \n\n";
        }
    }

    private class EmptyReferenceException extends LoadQueryException {
        public EmptyReferenceException(String s) {
            super(s);
            ERROR_MESSAGE = "Element not found. Empty reference: #";
        }
    }
}