Java tutorial
/* * Copyright 2011 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.resultview; import annis.CommonHelper; import annis.libgui.Background; import annis.libgui.Helper; import annis.libgui.InstanceConfig; import annis.libgui.PluginSystem; import annis.libgui.VisualizationToggle; import annis.libgui.media.MediaController; import annis.libgui.media.MediaPlayer; import annis.libgui.media.PDFViewer; import annis.libgui.visualizers.FilteringVisualizerPlugin; import annis.libgui.visualizers.VisualizerInput; import annis.libgui.visualizers.VisualizerPlugin; import annis.resolver.ResolverEntry; import annis.visualizers.LoadableVisualizer; import com.google.common.base.Joiner; import com.google.common.escape.Escaper; import com.google.common.net.UrlEscapers; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.vaadin.server.FontAwesome; import com.vaadin.server.Resource; import com.vaadin.server.StreamResource; import com.vaadin.server.VaadinSession; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.Notification; import com.vaadin.ui.ProgressBar; import com.vaadin.ui.UI; import com.vaadin.ui.themes.ChameleonTheme; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.corpus_tools.salt.common.SDocument; import org.corpus_tools.salt.common.SDocumentGraph; import org.corpus_tools.salt.common.SToken; 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; /** * Controls the visibility of visualizer plugins and provides some control * methods for the media visualizers. * * @author Thomas Krause <krauseto@hu-berlin.de> * @author Benjamin Weienfels <b.pixeldrama@gmail.com> * */ public class VisualizerPanel extends CssLayout implements Button.ClickListener, VisualizationToggle { public static final long serialVersionUID = 2L; private final Logger log = LoggerFactory.getLogger(VisualizerPanel.class); public static final Resource ICON_COLLAPSE = FontAwesome.MINUS_SQUARE_O; public static final Resource ICON_EXPAND = FontAwesome.PLUS_SQUARE_O; private String corpusName; private String documentName; private Component vis; private SDocument result; private PluginSystem ps; private ResolverEntry entry; private Map<String, Long> markedAndCovered; private Map<String, String> markersExact; private Map<String, String> markersCovered; private Button btEntry; private String htmlID; private String resultID; private VisualizerPlugin visPlugin; private Set<String> visibleTokenAnnos; private String segmentationName; private final String PERMANENT = "permanent"; private final String ISVISIBLE = "visible"; private final String HIDDEN = "hidden"; private final String PRELOADED = "preloaded"; private ProgressBar progress; private InstanceConfig instanceConfig; private VisualizerContextChanger visCtxChanger; private final static Escaper urlPathEscape = UrlEscapers.urlPathSegmentEscaper(); /** * This Constructor should be used for {@link ComponentVisualizerPlugin} * Visualizer. * */ public VisualizerPanel(final ResolverEntry entry, SDocument result, String corpusName, String documentName, Set<String> visibleTokenAnnos, Map<String, Long> markedAndCovered, @Deprecated Map<String, String> markedAndCoveredMap, @Deprecated Map<String, String> markedExactMap, String htmlID, String resultID, SingleResultPanel parent, String segmentationName, PluginSystem ps, InstanceConfig instanceConfig) throws IOException { this.ps = ps; this.instanceConfig = instanceConfig; this.entry = entry; this.markersExact = markedExactMap; this.markersCovered = markedAndCoveredMap; this.visCtxChanger = parent; this.result = result; this.corpusName = corpusName; this.documentName = documentName; this.visibleTokenAnnos = visibleTokenAnnos; this.markedAndCovered = markedAndCovered; this.segmentationName = segmentationName; this.htmlID = htmlID; this.resultID = resultID; this.progress = new ProgressBar(); this.progress.setIndeterminate(true); this.progress.setVisible(false); this.progress.setEnabled(false); this.addStyleName(ChameleonTheme.PANEL_BORDERLESS); this.setWidth("100%"); if (entry != null && ps != null) { visPlugin = ps.getVisualizer(entry.getVisType()); if (visPlugin == null) { // fallback to default visualizer if original vis type was not found entry.setVisType(PluginSystem.DEFAULT_VISUALIZER); visPlugin = ps.getVisualizer(entry.getVisType()); } if (HIDDEN.equalsIgnoreCase(entry.getVisibility())) { // build button for visualizer btEntry = new Button(entry.getDisplayName()); btEntry.setIcon(ICON_EXPAND); btEntry.setStyleName(ChameleonTheme.BUTTON_BORDERLESS + " " + ChameleonTheme.BUTTON_SMALL); btEntry.addClickListener((Button.ClickListener) this); btEntry.setDisableOnClick(true); addComponent(btEntry); addComponent(progress); } else { if (ISVISIBLE.equalsIgnoreCase(entry.getVisibility()) || PRELOADED.equalsIgnoreCase(entry.getVisibility())) { // build button for visualizer btEntry = new Button(entry.getDisplayName()); btEntry.setIcon(ICON_COLLAPSE); btEntry.setStyleName(ChameleonTheme.BUTTON_BORDERLESS + " " + ChameleonTheme.BUTTON_SMALL); btEntry.addClickListener((Button.ClickListener) this); addComponent(btEntry); } addComponent(progress); // create the visualizer and calc input try { vis = createComponent(); if (vis != null) { vis.setVisible(true); addComponent(vis); } } catch (Exception ex) { Notification.show("Could not create visualizer " + visPlugin.getShortName(), ex.toString(), Notification.Type.TRAY_NOTIFICATION); log.error("Could not create visualizer " + visPlugin.getShortName(), ex); } if (btEntry != null && PRELOADED.equalsIgnoreCase(entry.getVisibility())) { btEntry.setIcon(ICON_EXPAND); if (vis != null) { vis.setVisible(false); } } } } // end if entry not null } private List<SToken> createTokenList(List<String> tokenIDs, SDocumentGraph graph) { if (tokenIDs == null || graph == null) { return new LinkedList<>(); } ArrayList<SToken> r = new ArrayList<>(tokenIDs.size()); for (String t : tokenIDs) { SNode n = graph.getNode(t); if (n instanceof SToken) { r.add((SToken) n); } } return r; } private Component createComponent() { if (visPlugin == null) { return null; } final VisualizerInput input = createInput(); Component c = visPlugin.createComponent(input, this); c.setVisible(false); c.addStyleName(Helper.CORPUS_FONT); c.addStyleName("vis-content"); return c; } private VisualizerInput createInput() { VisualizerInput input = new VisualizerInput(); input.setAnnisWebServiceURL((String) VaadinSession.getCurrent().getAttribute("AnnisWebService.URL")); input.setContextPath(Helper.getContext()); input.setDotPath((String) VaadinSession.getCurrent().getAttribute("DotPath")); input.setId(resultID); input.setMarkableExactMap(markersExact); input.setMarkableMap(markersCovered); input.setMarkedAndCovered(markedAndCovered); input.setResult(result); input.setVisibleTokenAnnos(visibleTokenAnnos); input.setSegmentationName(segmentationName); if (instanceConfig != null && instanceConfig.getFont() != null) { input.setFont(instanceConfig.getFont()); } if (entry != null) { input.setMappings(entry.getMappings()); input.setNamespace(entry.getNamespace()); String template = Helper.getContext() + "/Resource/" + entry.getVisType() + "/%s"; input.setResourcePathTemplate(template); } // getting the whole document, when plugin is using text if (visPlugin != null && visPlugin.isUsingText() && result != null && result.getDocumentGraph().getNodes().size() > 0) { List<String> nodeAnnoFilter = null; if (visPlugin instanceof FilteringVisualizerPlugin) { nodeAnnoFilter = ((FilteringVisualizerPlugin) visPlugin).getFilteredNodeAnnotationNames(corpusName, documentName, input.getMappings()); } SaltProject p = getDocument(result.getGraph().getRoots().get(0).getName(), result.getName(), nodeAnnoFilter); SDocument wholeDocument = p.getCorpusGraphs().get(0).getDocuments().get(0); input.setDocument(wholeDocument); } else { input.setDocument(result); } // getting the raw text, when the visualizer wants to have it if (visPlugin != null && visPlugin.isUsingRawText()) { input.setRawText(Helper.getRawText(corpusName, documentName)); } return input; } public void setVisibleTokenAnnosVisible(SortedSet<String> annos) { this.visibleTokenAnnos = annos; if (visPlugin != null && vis != null) { visPlugin.setVisibleTokenAnnosVisible(vis, annos); } } public void setSegmentationLayer(String segmentationName, Map<String, Long> markedAndCovered) { this.segmentationName = segmentationName; this.markedAndCovered = markedAndCovered; if (visPlugin != null && vis != null) { visPlugin.setSegmentationLayer(vis, segmentationName, markedAndCovered); } } private SaltProject getDocument(String toplevelCorpusName, String documentName, List<String> nodeAnnoFilter) { SaltProject txt = null; try { toplevelCorpusName = urlPathEscape.escape(toplevelCorpusName); documentName = urlPathEscape.escape(documentName); WebResource res = Helper.getAnnisWebResource().path("query").path("graph").path(toplevelCorpusName) .path(documentName); if (nodeAnnoFilter != null) { res = res.queryParam("filternodeanno", Joiner.on(",").join(nodeAnnoFilter)); } txt = res.get(SaltProject.class); } catch (ClientHandlerException | UniformInterfaceException e) { log.error("General remote service exception", e); } return txt; } @Override public void buttonClick(ClickEvent event) { boolean isVisible = !visualizerIsVisible(); // register new state by the parent SingleResultPanel, so the state will be // still available, after a reload visCtxChanger.registerVisibilityStatus(entry.getId(), isVisible); // start the toogle process. toggleVisualizer(isVisible, null); } @Override public boolean visualizerIsVisible() { if (vis == null || !vis.isVisible()) { return false; } return true; } private void loadVisualizer(final LoadableVisualizer.Callback callback) { if (visPlugin != null) { btEntry.setIcon(ICON_COLLAPSE); progress.setIndeterminate(true); progress.setVisible(true); progress.setEnabled(true); progress.setDescription("Loading visualizer" + visPlugin.getShortName()); ExecutorService execService = Executors.newSingleThreadExecutor(); final Future<Component> future = execService.submit(new LoadComponentTask()); // run the actual code to load the visualizer Background.run(new BackgroundJob(future, callback, UI.getCurrent())); } // end if create input was needed } // end loadVisualizer private void updateGUIAfterLoadingVisualizer(LoadableVisualizer.Callback callback) { if (callback != null && vis instanceof LoadableVisualizer) { LoadableVisualizer loadableVis = (LoadableVisualizer) vis; if (loadableVis.isLoaded()) { // direct call callback since the visualizer is already ready callback.visualizerLoaded(loadableVis); } else { loadableVis.clearCallbacks(); // add listener when player was fully loaded loadableVis.addOnLoadCallBack(callback); } } progress.setEnabled(false); progress.setVisible(false); if (vis != null) { btEntry.setEnabled(true); vis.setVisible(true); if (vis instanceof PDFViewer) { ((PDFViewer) vis).openPDFPage("-1"); } if (vis instanceof MediaPlayer) { // if this is a media player visualizer, close all other media players // since some browsers (e.g. Chrome) have problems if there are multiple // audio/video elements on one page MediaController mediaController = VaadinSession.getCurrent().getAttribute(MediaController.class); mediaController.closeOtherPlayers((MediaPlayer) vis); } // add if not already added if (getComponentIndex(vis) < 0) { addComponent(vis); } } } @Override public void toggleVisualizer(boolean visible, LoadableVisualizer.Callback callback) { if (visible) { loadVisualizer(callback); } else { // hide btEntry.setEnabled(true); if (vis != null) { vis.setVisible(false); if (vis instanceof MediaPlayer) { removeComponent(vis); } } btEntry.setIcon(ICON_EXPAND); } } public String getHtmlID() { return htmlID; } private class BackgroundJob implements Runnable { private final Future<Component> future; private final LoadableVisualizer.Callback callback; private final UI ui; public BackgroundJob(Future<Component> future, LoadableVisualizer.Callback callback, UI ui) { this.future = future; this.callback = callback; this.ui = ui; } @Override public void run() { Throwable exception = null; try { final Component result = future.get(60, TimeUnit.SECONDS); ui.accessSynchronously(new Runnable() { @Override public void run() { vis = result; updateGUIAfterLoadingVisualizer(callback); } }); } catch (InterruptedException ex) { log.error(null, ex); exception = ex; } catch (ExecutionException ex) { log.error(null, ex); exception = ex; } catch (TimeoutException ex) { future.cancel(true); log.error("Could create visualizer " + visPlugin.getShortName() + " in 60 seconds: Timeout", ex); exception = ex; } if (exception != null) { final Throwable finalException = exception; ui.accessSynchronously(new Runnable() { @Override public void run() { Notification.show("Error when creating visualizer " + visPlugin.getShortName(), finalException.toString(), Notification.Type.WARNING_MESSAGE); } }); } } } public class LoadComponentTask implements Callable<Component> { @Override public Component call() throws Exception { // only create component if not already created if (vis == null) { return createComponent(); } else { return vis; } } } public static class ByteArrayOutputStreamSource implements StreamResource.StreamSource { private static final Logger log = LoggerFactory.getLogger(ByteArrayOutputStreamSource.class); private transient ByteArrayOutputStream byteStream; public ByteArrayOutputStreamSource(ByteArrayOutputStream byteStream) { this.byteStream = byteStream; } @Override public InputStream getStream() { if (byteStream == null) { log.error("byte stream was null"); return null; } return new ByteArrayInputStream(byteStream.toByteArray()); } } public String getVisualizerShortName() { if (visPlugin != null) { return visPlugin.getShortName(); } else { return null; } } protected SDocument getResult() { return result; } }