annis.gui.EmbeddedVisUI.java Source code

Java tutorial

Introduction

Here is the source code for annis.gui.EmbeddedVisUI.java

Source

/*
 * Copyright 2015 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;

import annis.CommonHelper;
import annis.gui.docbrowser.DocBrowserController;
import annis.gui.util.ANNISFontIcon;
import annis.libgui.AnnisUser;
import annis.libgui.Background;
import annis.libgui.Helper;
import annis.libgui.IDGenerator;
import annis.libgui.InstanceConfig;
import annis.libgui.LoginDataLostException;
import annis.libgui.visualizers.VisualizerInput;
import annis.libgui.visualizers.VisualizerPlugin;
import static annis.model.AnnisConstants.ANNIS_NS;
import static annis.model.AnnisConstants.FEAT_MATCHEDANNOS;
import static annis.model.AnnisConstants.FEAT_MATCHEDIDS;
import static annis.model.AnnisConstants.FEAT_MATCHEDNODE;
import annis.service.objects.Match;
import annis.service.objects.Visualizer;
import annis.visualizers.htmlvis.HTMLVis;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.util.concurrent.FutureCallback;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.server.ExternalResource;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Component;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Link;
import com.vaadin.ui.Panel;
import com.vaadin.ui.ProgressBar;
import com.vaadin.ui.VerticalLayout;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import javax.ws.rs.core.Response;
import org.corpus_tools.salt.SaltFactory;
import org.corpus_tools.salt.common.SCorpusGraph;
import org.corpus_tools.salt.common.SDocument;
import org.corpus_tools.salt.common.SaltProject;
import org.corpus_tools.salt.core.SFeature;
import org.corpus_tools.salt.core.SNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Thomas Krause <krauseto@hu-berlin.de>
 */
@Theme("annis_embeddedvis")
@Push(value = PushMode.AUTOMATIC, transport = Transport.LONG_POLLING)
public class EmbeddedVisUI extends CommonUI {

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

    public static final String URL_PREFIX = "/embeddedvis";

    public static final String KEY_PREFIX = "embedded_";

    public static final String KEY_SALT = KEY_PREFIX + "salt";

    public static final String KEY_NAMESPACE = KEY_PREFIX + "ns";

    public static final String KEY_SEARCH_INTERFACE = KEY_PREFIX + "interface";

    public static final String KEY_BASE_TEXT = KEY_PREFIX + "base";

    public static final String KEY_MATCH = KEY_PREFIX + "match";

    public static final String KEY_INSTANCE = KEY_PREFIX + "instance";

    public EmbeddedVisUI() {
        super(URL_PREFIX);
    }

    @Override
    protected void init(VaadinRequest request) {
        super.init(request);

        String rawPath = request.getPathInfo();
        List<String> splittedPath = new LinkedList<>();
        if (rawPath != null) {
            rawPath = rawPath.substring(URL_PREFIX.length());
            splittedPath = Splitter.on("/").omitEmptyStrings().trimResults().limit(3).splitToList(rawPath);
        }

        if (splittedPath.size() == 1) {
            // a visualizer definition which get the results from a remote salt file
            String saltUrl = request.getParameter(KEY_SALT);
            if (saltUrl == null) {
                displayGeneralHelp();
            } else {
                generateVisFromRemoteURL(splittedPath.get(0), saltUrl, request.getParameterMap());
            }
        } else if (splittedPath.size() >= 3) {
            // a visualizer definition visname/corpusname/documentname
            if ("htmldoc".equals(splittedPath.get(0))) {
                showHtmlDoc(splittedPath.get(1), splittedPath.get(2), request.getParameterMap());
            } else {
                displayMessage("Unknown visualizer \"" + splittedPath.get(0) + "\"",
                        "Only \"htmldoc\" is supported yet.");
            }
        } else {
            displayGeneralHelp();
        }
        addStyleName("loaded-embedded-vis");
    }

    private void displayGeneralHelp() {
        displayMessage("Path not complete",
                "You have to specify what visualizer to use and which document of which corpus you want to visualizer by giving the correct path:<br />"
                        + "<code>http://example.com/annis/embeddedvis/&lt;vis&gt;/&lt;corpus&gt;/&lt;doc&gt;</code>"
                        + "<ul>"
                        + "<li><code>vis</code>: visualizer name (currently only \"htmldoc\" is supported)</li>"
                        + "<li><code>corpus</code>: corpus name</li>"
                        + "<li><code>doc</code>: name of the document to visualize</li>" + "</ul>");
    }

    private void generateVisFromRemoteURL(final String visName, final String rawUri, Map<String, String[]> args) {
        try {
            // find the matching visualizer
            final VisualizerPlugin visPlugin = this.getVisualizer(visName);
            if (visPlugin == null) {
                displayMessage("Unknown visualizer \"" + visName + "\"",
                        "This ANNIS instance does not know the given visualizer.");
                return;
            }

            URI uri = new URI(rawUri);
            // fetch content of the URI
            Client client = null;
            AnnisUser user = Helper.getUser();
            if (user != null) {
                client = user.getClient();
            }
            if (client == null) {
                client = Helper.createRESTClient();
            }
            final WebResource saltRes = client.resource(uri);

            displayLoadingIndicator();

            // copy the arguments for using them later in the callback
            final Map<String, String[]> argsCopy = new LinkedHashMap<>(args);

            Background.runWithCallback(new Callable<SaltProject>() {
                @Override
                public SaltProject call() throws Exception {
                    return saltRes.get(SaltProject.class);
                }
            }, new FutureCallback<SaltProject>() {
                @Override
                public void onFailure(Throwable t) {
                    displayMessage("Could not query the result.", t.getMessage());
                }

                @Override
                public void onSuccess(SaltProject p) {
                    // TODO: allow to display several visualizers when there is more than one document
                    SCorpusGraph firstCorpusGraph = null;
                    SDocument doc = null;
                    if (p.getCorpusGraphs() != null && !p.getCorpusGraphs().isEmpty()) {
                        firstCorpusGraph = p.getCorpusGraphs().get(0);
                        if (firstCorpusGraph.getDocuments() != null && !firstCorpusGraph.getDocuments().isEmpty()) {
                            doc = firstCorpusGraph.getDocuments().get(0);
                        }
                    }
                    if (doc == null) {
                        displayMessage("No documents found in provided URL.", "");
                        return;
                    }

                    if (argsCopy.containsKey(KEY_INSTANCE)) {
                        Map<String, InstanceConfig> allConfigs = loadInstanceConfig();
                        InstanceConfig newConfig = allConfigs.get(argsCopy.get(KEY_INSTANCE)[0]);
                        if (newConfig != null) {
                            setInstanceConfig(newConfig);
                        }
                    }
                    // now it is time to load the actual defined instance fonts
                    loadInstanceFonts();

                    // generate the visualizer
                    VisualizerInput visInput = new VisualizerInput();
                    visInput.setDocument(doc);
                    if (getInstanceConfig() != null && getInstanceConfig().getFont() != null) {
                        visInput.setFont(getInstanceFont());
                    }
                    Properties mappings = new Properties();
                    for (Map.Entry<String, String[]> e : argsCopy.entrySet()) {
                        if (!KEY_SALT.equals(e.getKey()) && e.getValue().length > 0) {
                            mappings.put(e.getKey(), e.getValue()[0]);
                        }
                    }
                    visInput.setMappings(mappings);
                    String[] namespace = argsCopy.get(KEY_NAMESPACE);
                    if (namespace != null && namespace.length > 0) {
                        visInput.setNamespace(namespace[0]);
                    } else {
                        visInput.setNamespace(null);
                    }

                    String baseText = null;
                    if (argsCopy.containsKey(KEY_BASE_TEXT)) {
                        String[] value = argsCopy.get(KEY_BASE_TEXT);
                        if (value.length > 0) {
                            baseText = value[0];
                        }
                    }

                    List<SNode> segNodes = CommonHelper.getSortedSegmentationNodes(baseText,
                            doc.getDocumentGraph());

                    if (argsCopy.containsKey(KEY_MATCH)) {
                        String[] rawMatch = argsCopy.get(KEY_MATCH);
                        if (rawMatch.length > 0) {
                            // enhance the graph with match information from the arguments
                            Match match = Match.parseFromString(rawMatch[0]);
                            addMatchToDocumentGraph(match, doc);
                        }
                    }

                    Map<String, String> markedColorMap = new HashMap<>();
                    Map<String, String> exactMarkedMap = Helper.calculateColorsForMarkedExact(doc);
                    Map<String, Long> markedAndCovered = Helper.calculateMarkedAndCoveredIDs(doc, segNodes,
                            baseText);
                    Helper.calulcateColorsForMarkedAndCovered(doc, markedAndCovered, markedColorMap);
                    visInput.setMarkedAndCovered(markedAndCovered);
                    visInput.setMarkableMap(markedColorMap);
                    visInput.setMarkableExactMap(exactMarkedMap);
                    visInput.setContextPath(Helper.getContext());
                    String template = Helper.getContext() + "/Resource/" + visName + "/%s";
                    visInput.setResourcePathTemplate(template);
                    visInput.setSegmentationName(baseText);
                    // TODO: which other thing do we have to provide?

                    Component c = visPlugin.createComponent(visInput, null);
                    // add the styles
                    c.addStyleName("corpus-font");
                    c.addStyleName("vis-content");

                    Link link = new Link();
                    link.setCaption("Show in ANNIS search interface");
                    link.setIcon(ANNISFontIcon.LOGO);
                    link.setVisible(false);
                    link.addStyleName("dontprint");
                    link.setTargetName("_blank");
                    if (argsCopy.containsKey(KEY_SEARCH_INTERFACE)) {
                        String[] interfaceLink = argsCopy.get(KEY_SEARCH_INTERFACE);
                        if (interfaceLink.length > 0) {
                            link.setResource(new ExternalResource(interfaceLink[0]));
                            link.setVisible(true);
                        }
                    }
                    VerticalLayout layout = new VerticalLayout(link, c);
                    layout.setComponentAlignment(link, Alignment.TOP_LEFT);
                    layout.setSpacing(true);
                    layout.setMargin(true);

                    setContent(layout);

                    IDGenerator.assignID(link);
                }

            });

        } catch (URISyntaxException ex) {
            displayMessage("Invalid URL", "The provided URL is malformed:<br />" + ex.getMessage());
        } catch (LoginDataLostException ex) {
            displayMessage("LoginData Lost",
                    "No login data available any longer in the session:<br /> " + ex.getMessage());
        } catch (UniformInterfaceException ex) {
            if (ex.getResponse().getStatus() == Response.Status.FORBIDDEN.getStatusCode()) {
                displayMessage("Corpus access forbidden",
                        "You are not allowed to access this corpus. "
                                + "Please login at the <a target=\"_blank\" href=\"" + Helper.getContext()
                                + "\">main application</a> first and then reload this page.");
            } else {
                displayMessage("Service error", ex.getMessage());
            }
        } catch (ClientHandlerException ex) {
            displayMessage("Could not generate the visualization because the ANNIS service reported an error.",
                    ex.getMessage());
        } catch (Throwable ex) {
            displayMessage("Could not generate the visualization.",
                    ex.getMessage() == null
                            ? ("An unknown error of type " + ex.getClass().getSimpleName()) + " occured."
                            : ex.getMessage());
        }
    }

    private void addMatchToDocumentGraph(Match match, SDocument document) {
        List<String> allUrisAsString = new LinkedList<>();
        long i = 1;
        for (URI u : match.getSaltIDs()) {
            allUrisAsString.add(u.toASCIIString());
            SNode matchedNode = document.getDocumentGraph().getNode(u.toASCIIString());
            // set the feature for this specific node
            if (matchedNode != null) {
                SFeature existing = matchedNode.getFeature(ANNIS_NS, FEAT_MATCHEDNODE);
                if (existing == null) {
                    SFeature featMatchedNode = SaltFactory.createSFeature();
                    featMatchedNode.setNamespace(ANNIS_NS);
                    featMatchedNode.setName(FEAT_MATCHEDNODE);
                    featMatchedNode.setValue(i);
                    matchedNode.addFeature(featMatchedNode);
                }
            }
            i++;
        }

        SFeature featIDs = SaltFactory.createSFeature();
        featIDs.setNamespace(ANNIS_NS);
        featIDs.setName(FEAT_MATCHEDIDS);
        featIDs.setValue(Joiner.on(",").join(allUrisAsString));
        document.addFeature(featIDs);

        SFeature featAnnos = SaltFactory.createSFeature();
        featAnnos.setNamespace(ANNIS_NS);
        featAnnos.setName(FEAT_MATCHEDANNOS);
        featAnnos.setValue(Joiner.on(",").join(match.getAnnos()));
        document.addFeature(featAnnos);

    }

    private void showHtmlDoc(String corpus, String doc, Map<String, String[]> args) {
        // do nothing for empty fragments
        if (args == null || args.isEmpty()) {
            return;
        }

        if (args.get("config") != null && args.get("config").length > 0) {
            String config = args.get("config")[0];

            // get input parameters
            HTMLVis visualizer;
            visualizer = new HTMLVis();

            VisualizerInput input;
            Visualizer visConfig;
            visConfig = new Visualizer();
            visConfig.setDisplayName(" ");
            visConfig.setMappings("config:" + config);
            visConfig.setNamespace(null);
            visConfig.setType("htmldoc");

            //create input
            try {
                input = DocBrowserController.createInput(corpus, doc, visConfig, false, null);
                //create components, put in a panel
                Panel viszr = visualizer.createComponent(input, null);

                // Set the panel as the content of the UI
                setContent(viszr);
            } catch (UniformInterfaceException ex) {
                displayMessage("Could not query document", "error was \"" + ex.getMessage()
                        + "\" (detailed error is available in the server log-files)");
                log.error("Could not get document for embedded visualizer", ex);
            }

        } else {
            displayMessage("Missing required argument for visualizer \"htmldoc\"",
                    "The following arguments are required:" + "<ul>"
                            + "<li><code>config</code>: the internal config file to use (same as <a href=\"http://korpling.github.io/ANNIS/doc/classannis_1_1visualizers_1_1htmlvis_1_1HTMLVis.html\">\"config\" mapping parameter)</a></li>"
                            + "</ul>");
        }
    }

    private void displayLoadingIndicator() {
        VerticalLayout layout = new VerticalLayout();

        layout.addStyleName("v-app-loading");
        layout.setSizeFull();

        setContent(layout);
    }

    private void displayMessage(String header, String content) {
        Label label = new Label("<h1>" + header + "</h1>" + "<div>" + content + "</div>", ContentMode.HTML);
        label.setSizeFull();
        setContent(label);
    }

}