Java tutorial
/* * 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/<vis>/<corpus>/<doc></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); } }