annis.gui.components.codemirror.AqlCodeEditor.java Source code

Java tutorial

Introduction

Here is the source code for annis.gui.components.codemirror.AqlCodeEditor.java

Source

/*
 * Copyright 2014 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.components.codemirror;

import annis.model.AqlParseError;
import annis.model.QueryNode;
import com.vaadin.annotations.JavaScript;
import com.vaadin.annotations.StyleSheet;
import com.vaadin.data.Property;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.event.FieldEvents;
import com.vaadin.ui.AbstractJavaScriptComponent;
import com.vaadin.ui.JavaScriptFunction;
import elemental.json.JsonArray;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A code editor component for the ANNIQ Query Language.
 *
 * @author Thomas Krause <krauseto@hu-berlin.de>
 */
@JavaScript({ "vaadin://jquery.js", "lib/codemirror.js", "mode/aql/aql.js", "lib/edit/matchbrackets.js",
        "lib/lint/lint.js", "lib/display/placeholder.js", "AqlCodeEditor.js" })
@StyleSheet({ "lib/codemirror.css", "lib/lint/lint.css", "AqlCodeEditor.css" })
public class AqlCodeEditor extends AbstractJavaScriptComponent
        implements FieldEvents.TextChangeNotifier, Property.Viewer, Property.ValueChangeListener {

    private static final Logger log = LoggerFactory.getLogger(AqlCodeEditor.class);

    private int timeout;

    private Property<String> dataSource;

    public AqlCodeEditor() {
        addFunction("textChanged", new TextChangedFunction());
        addStyleName("aql-code-editor");

        AqlCodeEditor.this.setPropertyDataSource(new ObjectProperty("", String.class));

        // init to one so the client (which starts with 0) at initialization always uses
        // the the values provided by the server state
        AqlCodeEditor.this.getState().serverRequestCounter = 1;
    }

    @Override
    public void setPropertyDataSource(Property newDataSource) {
        if (newDataSource == null) {
            throw new IllegalArgumentException("Data source must not be null");
        }

        if (this.dataSource instanceof Property.ValueChangeNotifier) {
            ((Property.ValueChangeNotifier) this.dataSource).removeValueChangeListener(this);
        }

        this.dataSource = newDataSource;

        if (newDataSource instanceof Property.ValueChangeNotifier) {
            ((Property.ValueChangeNotifier) this.dataSource).addValueChangeListener(this);
        }

    }

    @Override
    public Property getPropertyDataSource() {
        return this.dataSource;
    }

    @Override
    public void valueChange(Property.ValueChangeEvent event) {
        log.debug("valueChange \"{}\"/\"{}", event.getProperty().getValue(), this.dataSource.getValue());
        String oldText = getState().text;
        String newText = this.dataSource.getValue();

        if (oldText == null || !oldText.equals(newText)) {
            getState().text = newText;
            // this is a server side state change and we have to explicitly tell the client we want to change the text
            getState().serverRequestCounter++;

            log.debug("invalidating \"{}\"/\"{}\"", oldText, getState().text);
            markAsDirty();
        }
    }

    private class TextChangedFunction implements JavaScriptFunction {

        @Override
        public void call(JsonArray args) throws JSONException {
            log.debug("TextChangedFunction \"{}\"", args.getString(0));
            getState().text = args.getString(0);
            getPropertyDataSource().setValue(args.getString(0));

            final String textCopy = dataSource.getValue();
            final int cursorPos = (int) args.getNumber(1);
            fireEvent(new FieldEvents.TextChangeEvent(AqlCodeEditor.this) {

                @Override
                public String getText() {
                    return textCopy;
                }

                @Override
                public int getCursorPosition() {
                    return cursorPos;
                }
            });
        }
    }

    public void setNodes(List<QueryNode> nodes) {
        getState().nodeMappings.clear();
        if (nodes != null) {
            getState().nodeMappings.putAll(mapQueryNodes(nodes));
        }
    }

    private TreeMap<String, Integer> mapQueryNodes(List<QueryNode> nodes) {
        TreeMap<String, Integer> result = new TreeMap<>();
        Map<Integer, TreeSet<Long>> alternative2Nodes = new HashMap<>();

        for (QueryNode n : nodes) {
            TreeSet<Long> orderedNodeSet = alternative2Nodes.get(n.getAlternativeNumber());
            if (orderedNodeSet == null) {
                orderedNodeSet = new TreeSet<>();
                alternative2Nodes.put(n.getAlternativeNumber(), orderedNodeSet);
            }
            orderedNodeSet.add(n.getId());
        }

        for (TreeSet<Long> orderedNodeSet : alternative2Nodes.values()) {
            int newID = 1;
            for (long var : orderedNodeSet) {
                result.put("" + var, newID);
                newID++;
            }
        }

        return result;
    }

    public void setInputPrompt(String prompt) {
        getState().inputPrompt = prompt;
        markAsDirty();
    }

    public void setTextChangeTimeout(int timeout) {
        callFunction("setChangeDelayTime", timeout);
        this.timeout = timeout;
    }

    public int getTextChangeTimeout() {
        return this.timeout;
    }

    @Override
    public void addTextChangeListener(FieldEvents.TextChangeListener listener) {
        addListener(FieldEvents.TextChangeListener.EVENT_ID, FieldEvents.TextChangeEvent.class, listener,
                FieldEvents.TextChangeListener.EVENT_METHOD);
    }

    public String getTextareaStyle() {
        return getState().textareaClass == null ? "" : getState().textareaClass;
    }

    public void setTextareaStyle(String style) {
        getState().textareaClass = "".equals(style) ? null : style;
    }

    @Override
    public void addListener(FieldEvents.TextChangeListener listener) {
        addTextChangeListener(listener);
    }

    @Override
    public void removeTextChangeListener(FieldEvents.TextChangeListener listener) {
        removeListener(FieldEvents.TextChangeListener.EVENT_ID, FieldEvents.TextChangeEvent.class, listener);
    }

    @Override
    public void removeListener(FieldEvents.TextChangeListener listener) {
        removeTextChangeListener(listener);
    }

    public String getValue() {
        return dataSource.getValue();
    }

    public void setValue(String value) {
        dataSource.setValue(value);
    }

    @Override
    protected AqlCodeEditorState getState() {
        return (AqlCodeEditorState) super.getState();
    }

    public void setErrors(List<AqlParseError> errors) {
        getState().errors.clear();
        if (errors != null) {
            for (AqlParseError e : errors) {
                getState().errors.add(new AqlCodeEditorState.ParseError(e));
            }
        }
        markAsDirty();
    }

}