/*
* 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.gui.objects.PagedResultQuery;
import annis.libgui.Helper;
import annis.libgui.PluginSystem;
import annis.libgui.visualizers.FilteringVisualizerPlugin;
import annis.libgui.visualizers.VisualizerPlugin;
import annis.resolver.ResolverEntry;
import annis.service.objects.Match;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.escape.Escaper;
import com.google.common.net.UrlEscapers;
import com.vaadin.data.Property;
import com.vaadin.data.util.BeanItemContainer;
import com.vaadin.data.util.ObjectProperty;
import com.vaadin.event.SelectionEvent;
import com.vaadin.server.ExternalResource;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.BrowserFrame;
import com.vaadin.ui.Button;
import com.vaadin.ui.Grid;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
import com.vaadin.ui.themes.ValoTheme;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.ws.rs.core.UriBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Thomas Krause <krauseto@hu-berlin.de>
*/
public class ShareSingleMatchGenerator extends Window implements
SelectionEvent.SelectionListener
{
private final static Logger log = LoggerFactory.getLogger(ShareSingleMatchGenerator.class);
private final VerticalLayout layout;
private final Grid visSelector;
private final VerticalLayout generatedLinks;
private final Property<String> directURL;
private final Property<String> iframeCode;
private final BrowserFrame preview;
private final TextArea txtDirectURL;
private final TextArea txtIFrameCode;
private final BeanItemContainer<ResolverEntry> visContainer;
private final Match match;
private final PagedResultQuery query;
private final String segmentation;
private final PluginSystem ps;
private final Escaper urlParamEscaper = UrlEscapers.urlFormParameterEscaper();
public ShareSingleMatchGenerator(List<ResolverEntry> visualizers,
Match match,
PagedResultQuery query,
String segmentation,
PluginSystem ps)
{
this.match = match;
this.query = query;
this.segmentation = segmentation;
this.ps = ps;
setResizeLazy(true);
directURL = new ObjectProperty<>("");
iframeCode = new ObjectProperty<>("");
visContainer = new BeanItemContainer<>(ResolverEntry.class);
visContainer.addAll(visualizers);
txtDirectURL = new TextArea(directURL);
txtDirectURL.setCaption("Link for publications");
txtDirectURL.setWidth("100%");
txtDirectURL.setHeight("-1px");
txtDirectURL.addStyleName(ValoTheme.TEXTFIELD_LARGE);
txtDirectURL.addStyleName("shared-text");
txtDirectURL.setWordwrap(true);
txtDirectURL.setReadOnly(true);
txtIFrameCode = new TextArea(iframeCode);
txtIFrameCode.setCaption("Code for embedding visualization into web page");
txtIFrameCode.setWidth("100%");
txtIFrameCode.setHeight("-1px");
txtIFrameCode.addStyleName(ValoTheme.TEXTFIELD_LARGE);
txtIFrameCode.addStyleName("shared-text");
txtIFrameCode.setWordwrap(true);
txtIFrameCode.setReadOnly(true);
preview = new BrowserFrame();
preview.setCaption("Preview");
preview.addStyleName("shared-text");
preview.setSizeFull();
generatedLinks = new VerticalLayout(txtDirectURL, txtIFrameCode, preview);
generatedLinks.setComponentAlignment(txtDirectURL, Alignment.TOP_LEFT);
generatedLinks.setComponentAlignment(txtIFrameCode, Alignment.TOP_LEFT);
generatedLinks.setExpandRatio(preview, 1.0f);
visSelector = new Grid(visContainer);
visSelector.setCaption("Select visualization");
visSelector.setHeight("100%");
visSelector.setColumns("displayName");
visSelector.setSelectionMode(Grid.SelectionMode.SINGLE);
visSelector.addSelectionListener(ShareSingleMatchGenerator.this);
visSelector.select(visContainer.getIdByIndex(0));
visSelector.setWidth("300px");
visSelector.getColumn("displayName").setSortable(false);
generatedLinks.setSizeFull();
Label infoText = new Label(
"<p style=\"font-size: 18px\" >"
+ "<strong>Share your match:</strong> "
+ "1. Choose the visualization to share. 2. Copy the generated link or code. "
+ "3. Share this link with your peers or include the code in your website. "
+ "</p>",
ContentMode.HTML);
HorizontalLayout hLayout = new HorizontalLayout(visSelector, generatedLinks);
hLayout.setSizeFull();
hLayout.setSpacing(true);
hLayout.setExpandRatio(generatedLinks, 1.0f);
Button btClose = new Button("Close");
btClose.setSizeUndefined();
btClose.addClickListener(new Button.ClickListener()
{
@Override
public void buttonClick(Button.ClickEvent event)
{
getUI().removeWindow(ShareSingleMatchGenerator.this);
}
});
layout = new VerticalLayout(infoText, hLayout, btClose);
layout.setSizeFull();
layout.setExpandRatio(hLayout, 1.0f);
layout.setComponentAlignment(btClose, Alignment.MIDDLE_CENTER);
setContent(layout);
}
private URI generatorURLForVisualizer(ResolverEntry entry)
{
String appContext = Helper.getContext();
URI appURI = UI.getCurrent().getPage().getLocation();
UriBuilder result = UriBuilder.fromUri(appURI)
.replacePath(appContext)
.path("embeddedvis")
.path(Helper.encodeJersey(entry.getVisType()))
.fragment("");
if(entry.getNamespace() != null)
{
result = result.queryParam("embedded_ns",
Helper.encodeJersey(entry.getNamespace()));
}
// test if the request was made from a sub-instance
String nonContextPath = appURI.getPath().substring(appContext.length());
if(!nonContextPath.isEmpty())
{
if(nonContextPath.startsWith("/"))
{
nonContextPath = nonContextPath.substring(1);
}
result = result.queryParam(EmbeddedVisUI.KEY_INSTANCE, nonContextPath);
}
UriBuilder serviceURL =
UriBuilder.fromUri(Helper.getAnnisWebResource().path(
"query").getURI());
VisualizerPlugin visPlugin = ps.getVisualizer(entry.getVisType());
if(visPlugin != null && visPlugin.isUsingText())
{
// generate a service URL that gets the whole document
URI firstID = match.getSaltIDs().get(0);
String pathAsString = firstID.getRawPath();
List<String> path = Splitter.on('/').omitEmptyStrings().trimResults().splitToList(pathAsString);
String corpusName = path.get(0);
String documentName = path.get(path.size()-1);
try
{
corpusName = URLDecoder.decode(corpusName, "UTF-8");
documentName =URLDecoder.decode(documentName, "UTF-8"); ;
}
catch(UnsupportedEncodingException ex)
{
log.warn("Could not decode URL", ex);
}
// apply any node annotation filters if possible
if(visPlugin instanceof FilteringVisualizerPlugin)
{
List<String> visAnnos = ((FilteringVisualizerPlugin) visPlugin).getFilteredNodeAnnotationNames(
corpusName, documentName, entry.getMappings());
if(visAnnos != null)
{
Set<String> annos = new HashSet<>(visAnnos);
// always add the matched node annotation as well
for (String matchedAnno : match.getAnnos())
{
if(!matchedAnno.isEmpty())
{
annos.add(matchedAnno);
}
}
serviceURL = serviceURL.queryParam("filternodeanno", Joiner.on(",").
join(annos));
}
}
serviceURL = serviceURL.path("graph")
.path(Helper.encodePath(corpusName))
.path(Helper.encodePath(documentName));
// add the original match so the embedded visualizer can add it
// (since we use the graph query it will not be included in the Salt XMI itself)
result = result
.queryParam(EmbeddedVisUI.KEY_MATCH, Helper.encodeJersey(match.toString()));
}
else
{
// default to the subgraph URL for this specific match
serviceURL = serviceURL.path("search").path("subgraph")
.queryParam("match", Helper.encodeJersey(match.toString()))
.queryParam("left", query.getLeftContext())
.queryParam("right", query.getRightContext());
if(query.getSegmentation() != null)
{
serviceURL = serviceURL.queryParam("segmentation", query.getSegmentation());
}
}
// add the URL where to fetch the graph from
result = result.queryParam(EmbeddedVisUI.KEY_SALT,
Helper.encodeQueryParam(serviceURL.build().toASCIIString()));
// add the current view as "return back" parameter
result = result.queryParam(EmbeddedVisUI.KEY_SEARCH_INTERFACE,
appURI.toASCIIString());
if (segmentation != null)
{
result = result.queryParam(EmbeddedVisUI.KEY_BASE_TEXT, segmentation);
}
// add all mappings as parameter
for(String key : entry.getMappings().stringPropertyNames())
{
if(!key.startsWith(EmbeddedVisUI.KEY_PREFIX))
{
String value = Helper.encodeJersey(entry.getMappings().getProperty(key));
result = result.queryParam(key, value);
}
}
return result.build();
}
@Override
public void select(SelectionEvent event)
{
Set<Object> selected = event.getSelected();
if(selected.isEmpty())
{
generatedLinks.setVisible(false);
}
else
{
generatedLinks.setVisible(true);
URI url = generatorURLForVisualizer((ResolverEntry) selected.iterator().next());
String shortURL = Helper.shortenURL(url);
directURL.setValue(shortURL);
iframeCode.setValue("<iframe height=\"300px\" width=\"100%\" src=\"" + shortURL + "\"></iframe>");
preview.setSource(new ExternalResource(shortURL));
}
}
}