All Downloads are FREE. Search and download functionalities are using the official Maven repository.

annis.visualizers.htmlvis.HTMLVis Maven / Gradle / Ivy

There is a newer version: 4.0.0-beta.4
Show newest version
/*
 * Copyright 2013 SFB 632.
 *
 * 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.visualizers.htmlvis;

import annis.CommonHelper;
import annis.libgui.AnnisBaseUI;
import annis.libgui.Helper;
import annis.libgui.MatchedNodeColors;
import annis.libgui.VisualizationToggle;
import annis.libgui.visualizers.AbstractVisualizer;
import annis.libgui.visualizers.VisualizerInput;
import annis.model.Annotation;
import com.google.common.escape.Escaper;
import com.google.common.net.UrlEscapers;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.vaadin.shared.ui.label.ContentMode;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import net.xeoh.plugins.base.annotations.PluginImplementation;
import org.apache.commons.io.IOUtils;
import org.corpus_tools.salt.common.SDocumentGraph;
import org.corpus_tools.salt.common.SSpan;
import org.corpus_tools.salt.common.SToken;
import org.corpus_tools.salt.core.SNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * 

* Mappings:
*

    *
  • config - path of the visualization configuration file
  • *
  • hitmark - if "true" (which is the default) hit are marked in their * corresponding colors
  • *
*

* * @author Thomas Krause */ @PluginImplementation public class HTMLVis extends AbstractVisualizer { private static final Logger log = LoggerFactory.getLogger(HTMLVis.class); private final static Escaper urlPathEscape = UrlEscapers. urlPathSegmentEscaper(); private Map mc; private String tokenColor = ""; private boolean hitMark = true; @Override public String getShortName() { return "html"; } @Override public boolean isUsingText() { return false; } @Override public Panel createComponent(VisualizerInput vi, VisualizationToggle vt) { Panel scrollPanel = new Panel(); scrollPanel.setSizeFull(); Label lblResult = new Label("ERROR", ContentMode.HTML); lblResult.setSizeUndefined(); List corpusPath = CommonHelper.getCorpusPath(vi.getDocument(). getGraph(), vi.getDocument()); String corpusName = corpusPath.get(corpusPath.size() - 1); corpusName = urlPathEscape.escape(corpusName); String wrapperClassName = "annis-wrapped-htmlvis-" + corpusName.replaceAll("[^0-9A-Za-z-]", "_"); scrollPanel.addStyleName(wrapperClassName); String visConfigName = vi.getMappings().getProperty("config"); String hitMarkConfig = vi.getMappings().getProperty("hitmark", "true"); hitMark = Boolean.parseBoolean(hitMarkConfig); mc = vi.getMarkedAndCovered(); VisualizationDefinition[] definitions = parseDefinitions(corpusName, vi. getMappings()); if (definitions != null) { lblResult.setValue(createHTML(vi.getSResult().getDocumentGraph(), definitions)); String labelClass = vi.getMappings().getProperty("class", "htmlvis"); lblResult.addStyleName(labelClass); InputStream inStreamCSSRaw = null; if (visConfigName == null) { inStreamCSSRaw = HTMLVis.class.getResourceAsStream("htmlvis.css"); } else { WebResource resBinary = Helper.getAnnisWebResource().path( "query/corpora/").path(corpusName).path(corpusName) .path("binary").path(visConfigName + ".css"); ClientResponse response = resBinary.get(ClientResponse.class); if (response.getStatus() == ClientResponse.Status.OK.getStatusCode()) { inStreamCSSRaw = response.getEntityInputStream(); } } if (inStreamCSSRaw != null) { try (InputStream inStreamCSS = inStreamCSSRaw) { String cssContent = IOUtils.toString(inStreamCSS); UI currentUI = UI.getCurrent(); if (currentUI instanceof AnnisBaseUI) { // do not add identical CSS files ((AnnisBaseUI) currentUI).injectUniqueCSS(cssContent, wrapperClassName); } } catch (IOException ex) { log.error("Could not parse the HTML visualizer CSS file", ex); Notification.show( "Could not parse the HTML visualizer CSS file", ex. getMessage(), Notification.Type.ERROR_MESSAGE); } } } if (vi.getMappings().containsKey("debug")) { TextArea txtDebug = new TextArea(); txtDebug.setValue(lblResult.getValue()); txtDebug.setReadOnly(true); txtDebug.setWidth("100%"); Label sep = new Label("
", ContentMode.HTML); VerticalLayout layout = new VerticalLayout(txtDebug, sep, lblResult); layout.setSizeUndefined(); scrollPanel.setContent(layout); } else { scrollPanel.setContent(lblResult); } return scrollPanel; } @Override public List getFilteredNodeAnnotationNames(String toplevelCorpusName, String documentName, Properties mappings) { Set result = null; VisualizationDefinition[] definitions = parseDefinitions(toplevelCorpusName, mappings); if (definitions != null) { for (VisualizationDefinition def : definitions) { List sub = def.getMatcher().getRequiredAnnotationNames(); if (sub == null) { // a rule requires all annotations, abort result = null; break; } else { if (result == null) { result = new LinkedHashSet<>(); } result.addAll(sub); } } } if (result == null) { return null; } else { return new LinkedList<>(result); } } public VisualizationDefinition[] parseDefinitions(String toplevelCorpusName, Properties mappings) { InputStream inStreamConfigRaw = null; String visConfigName = mappings.getProperty("config"); if (visConfigName == null) { inStreamConfigRaw = HTMLVis.class.getResourceAsStream("defaultvis.config"); } else { WebResource resBinary = Helper.getAnnisWebResource().path( "query/corpora/").path(toplevelCorpusName).path(toplevelCorpusName) .path("binary").path(visConfigName + ".config"); ClientResponse response = resBinary.get(ClientResponse.class); if (response.getStatus() == ClientResponse.Status.OK.getStatusCode()) { inStreamConfigRaw = response.getEntityInputStream(); } } if (inStreamConfigRaw == null) { Notification.show("ERROR: html visualization configuration \"" + visConfigName + "\" not found in database", Notification.Type.ERROR_MESSAGE); } else { try (InputStream inStreamConfig = inStreamConfigRaw) { VisParser p = new VisParser(inStreamConfig); return p.getDefinitions(); } catch (IOException | VisParserException ex) { log.error("Could not parse the HTML visualization configuration file", ex); Notification.show( "Could not parse the HTML visualization configuration file", ex. getMessage(), Notification.Type.ERROR_MESSAGE); } } return null; } public String createHTML(SDocumentGraph graph, VisualizationDefinition[] definitions) { HashMap instruction_priorities = new HashMap<>(); SortedMap> outputStartTags = new TreeMap<>(); SortedMap> outputEndTags = new TreeMap<>(); StringBuilder sb = new StringBuilder(); List token = graph.getSortedTokenByText(); //Get metadata for visualizer if stylesheet requires it //First check the stylesheet Boolean bolMetaTypeFound = false; HashMap meta = new HashMap<>(); int def_priority = 0; for (VisualizationDefinition vis : definitions) { if (vis.getOutputter().getType() == SpanHTMLOutputter.Type.META_NAME) { bolMetaTypeFound = true; } else //not a meta-annotation, remember order in config file to set priority { if (vis.getMatcher() instanceof AnnotationNameMatcher) { instruction_priorities.put(vis, def_priority); } else if (vis.getMatcher() instanceof AnnotationNameAndValueMatcher) { instruction_priorities.put(vis, def_priority); } else if (vis.getMatcher() instanceof TokenMatcher) { instruction_priorities.put(vis, def_priority); } def_priority--; } vis.getOutputter().setMeta(meta); } if (bolMetaTypeFound == true) //Metadata is required, get corpus and document name { //Get corpus and document name String strDocName = ""; String strCorpName = ""; strDocName = graph.getDocument().getName(); List corpusPath = CommonHelper.getCorpusPath(graph.getDocument(). getGraph(), graph.getDocument()); strCorpName = corpusPath.get(corpusPath.size() - 1); //Get metadata and put in hashmap List metaData = Helper.getMetaDataDoc(strCorpName, strDocName); for (Annotation metaDatum : metaData) { meta.put(metaDatum.getName(), metaDatum.getValue()); } } for (SToken t : token) { tokenColor = ""; if (mc.containsKey(t) && hitMark) { tokenColor = MatchedNodeColors .getHTMLColorByMatch(mc.get(t)); } for (VisualizationDefinition vis : definitions) { String matched = vis.getMatcher().matchedAnnotation(t); if (matched != null) { vis.getOutputter().outputHTML(t, matched, outputStartTags, outputEndTags, tokenColor, Objects.firstNonNull(instruction_priorities.get(vis), 0)); } } } List spans = graph.getSpans(); for (VisualizationDefinition vis : definitions) { for (SSpan span : spans) { tokenColor = ""; if (mc.containsKey(span) && hitMark) { tokenColor = MatchedNodeColors .getHTMLColorByMatch(mc.get(span)); } String matched = vis.getMatcher().matchedAnnotation(span); if (matched != null) { vis.getOutputter().outputHTML(span, matched, outputStartTags, outputEndTags, tokenColor, Objects.firstNonNull(instruction_priorities.get(vis), 0)); } } } int minStartTagPos = outputStartTags.firstKey().intValue(); int maxEndTagPos = outputEndTags.lastKey().intValue(); //Find BEGIN and END instructions if available for (VisualizationDefinition vis : definitions) { if (vis.getMatcher() instanceof PseudoRegionMatcher) { PseudoRegionMatcher.PseudoRegion psdRegionType = ((PseudoRegionMatcher) vis. getMatcher()).getPsdRegion(); int positionStart = 0; int positionEnd = 0; if (!outputEndTags.isEmpty() && !outputStartTags.isEmpty() && psdRegionType != null) { switch (psdRegionType) { case BEGIN: positionStart = positionEnd = Integer.MIN_VALUE; // def_priority is now lower than all normal annotation instruction_priorities.put(vis, def_priority); break; case END: positionStart = positionEnd = Integer.MAX_VALUE; // def_priority is now lower than all normal annotation instruction_priorities.put(vis, def_priority); break; case ALL: // use same position as last and first key positionStart = minStartTagPos; positionEnd = maxEndTagPos; // The ALL pseudo-range must enclose everything, thus it get the // priority which is one lower than the smallest non BEGIN/END // priority. instruction_priorities.put(vis, def_priority); break; default: break; } } switch (vis.getOutputter().getType()) { case META_NAME: String strMetaVal = meta. get(vis.getOutputter().getMetaname().trim()); if (strMetaVal == null) { throw new NullPointerException( "no such metadata name in document: '" + vis.getOutputter(). getMetaname().trim() + "'"); } else { vis.getOutputter().outputAny(positionStart, positionEnd, ((PseudoRegionMatcher) vis.getMatcher()).getAnnotationName(), strMetaVal, outputStartTags, outputEndTags, Objects.firstNonNull(instruction_priorities.get(vis), 0)); } break; case CONSTANT: vis.getOutputter().outputAny(positionStart, positionEnd, ((PseudoRegionMatcher) vis.getMatcher()).getAnnotationName(), vis.getOutputter().getConstant(), outputStartTags, outputEndTags, Objects.firstNonNull(instruction_priorities.get(vis), 0)); break; case EMPTY: vis.getOutputter().outputAny(positionStart, positionEnd, ((PseudoRegionMatcher) vis.getMatcher()).getAnnotationName(), "", outputStartTags, outputEndTags, Objects.firstNonNull(instruction_priorities.get(vis), 0)); break; case ANNO_NAME: break; //this shouldn't happen, since the BEGIN/END instruction has no triggering annotation name or value case VALUE: break; //this shouldn't happen, since the BEGIN/END instruction has no triggering annotation name or value case ESCAPED_VALUE: break; //this shouldn't happen, since the BEGIN/END instruction has no triggering annotation name or value default: } } } // get all used indexes Set indexes = new TreeSet<>(); indexes.addAll(outputStartTags.keySet()); indexes.addAll(outputEndTags.keySet()); for (Long i : indexes) { // output all strings belonging to this token position // first the start tags for this position // add priorities from instruction_priorities for sorting length ties List unsortedStart = outputStartTags.get(i); SortedSet itemsStart = new TreeSet(); if (unsortedStart != null) { Iterator it = unsortedStart.iterator(); while (it.hasNext()) { OutputItem s = it.next(); itemsStart.add(s); } } { Iterator it = itemsStart.iterator(); boolean first = true; while (it.hasNext()) { OutputItem s = it.next(); if (!first) { sb.append("-->"); } first = false; sb.append(s.getOutputString()); if (it.hasNext()) { sb.append("