Java tutorial
/* * Copyright 2013 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.docbrowser; import annis.gui.MetaDataPanel; import annis.libgui.Helper; import annis.model.Annotation; import annis.service.objects.DocumentBrowserConfig; import annis.service.objects.MetaDataColumn; import annis.service.objects.OrderBy; import annis.service.objects.Visualizer; import com.google.common.escape.Escaper; import com.google.common.net.UrlEscapers; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.vaadin.data.Item; import com.vaadin.data.util.BeanItemContainer; import com.vaadin.data.util.IndexedContainer; import com.vaadin.server.FontAwesome; import com.vaadin.server.Resource; import com.vaadin.ui.Button; import com.vaadin.ui.Panel; import com.vaadin.ui.Table; import com.vaadin.ui.VerticalLayout; import com.vaadin.ui.Window; import com.vaadin.ui.themes.BaseTheme; import com.vaadin.ui.themes.ChameleonTheme; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * List documents for a specific corpus. * * @author Benjamin Weienfels<b.pixeldrama@gmail.com> */ public class DocBrowserTable extends Table { private Logger log = LoggerFactory.getLogger(DocBrowserTable.class); private final static Escaper urlPathEscape = UrlEscapers.urlPathSegmentEscaper(); private final DocBrowserPanel docBrowserPanel; private static final Resource INFO_ICON = FontAwesome.INFO_CIRCLE; /** * Represents the config of the doc visualizer. If there are meta data names * defined, also additional columns are generated */ private DocumentBrowserConfig docVisualizerConfig; // cache for doc meta data private final Map<String, Map<String, List<Annotation>>> docMetaDataCache; private IndexedContainer container; /** * Updates the table with docnames and generate the additional columns defined * by the user. * * @param docs the list of documents, wrapped in the {@link Annotation} POJO */ void setDocNames(List<Annotation> docs) { container = new IndexedContainer(); container.addContainerProperty("document name", String.class, "n/a"); MetaColumns metaCols = generateMetaColumns(); for (MetaDataCol metaDatum : metaCols.visibleColumns) { container.addContainerProperty(metaDatum.getColName(), String.class, "n/a"); } for (MetaDataCol metaDatum : metaCols.sortColumns) { container.addContainerProperty(metaDatum.getColName(), String.class, "n/a"); } container.addContainerProperty("corpus path", String.class, "n/a"); container.addContainerProperty("info", Button.class, null); container.addContainerProperty("visualizer", Panel.class, null); for (Annotation a : docs) { String doc = a.getName(); // reverse path and delete the brackets and set a new separator: // corpus > ... > subcorpus > document List<String> pathList = a.getAnnotationPath(); if (pathList == null) { pathList = new LinkedList<>(); } Collections.reverse(pathList); String path = StringUtils.join(pathList, " > "); // use corpus path for row id, since it should be unique by annis db schema Item row = container.addItem(path); if (row != null) { row.getItemProperty("document name").setValue(doc); // add the metadata columns. for (MetaDataCol metaDataCol : metaCols.visibleColumns) { String value = generateCell(a.getAnnotationPath(), metaDataCol); row.getItemProperty(metaDataCol.getColName()).setValue(value); } for (MetaDataCol metaDataCol : metaCols.sortColumns) { if (!metaCols.visibleColumns.contains(metaDataCol)) { // corpusName() holds the corpus path String value = generateCell(a.getAnnotationPath(), metaDataCol); row.getItemProperty(metaDataCol.getColName()).setValue(value); } } row.getItemProperty("corpus path").setValue(path); row.getItemProperty("visualizer").setValue(generateVisualizerLinks(doc)); row.getItemProperty("info").setValue(generateInfoButtonCell(doc)); } } setContainerDataSource(container); Object[] metaDataColNames = new Object[metaCols.visibleColumns.size()]; for (int i = 0; i < metaDataColNames.length; i++) { metaDataColNames[i] = metaCols.visibleColumns.get(i).getColName(); } Object[] columnNames = ArrayUtils.addAll( ArrayUtils.addAll(new Object[] { "document name" }, metaDataColNames), new Object[] { "corpus path", "visualizer", "info" }); setVisibleColumns(columnNames); for (Object colName : columnNames) { setColumnHeader((String) colName, (String) colName); } sortByMetaData(metaCols.sortColumns); } private MetaColumns generateMetaColumns() { MetaColumns metaColumns = new MetaColumns(); if (docVisualizerConfig == null) { return metaColumns; } if (docVisualizerConfig.getMetaDataColumns() != null) { MetaDataColumn[] metaDataCols = docVisualizerConfig.getMetaDataColumns(); for (MetaDataColumn metaDataCol : metaDataCols) { metaColumns.visibleColumns.add(new MetaDataCol(metaDataCol.getNamespace(), metaDataCol.getName())); } } if (docVisualizerConfig.getOrderBy() != null) { OrderBy[] orderBys = docVisualizerConfig.getOrderBy(); for (OrderBy orderBy : orderBys) { metaColumns.sortColumns .add(new MetaDataCol(orderBy.getNamespace(), orderBy.getName(), orderBy.isAscending())); } } return metaColumns; } private DocBrowserTable(DocBrowserPanel parent) { // the panel which contains this table this.docBrowserPanel = parent; // configure layout setSizeFull(); // put stripes to the table addStyleName(ChameleonTheme.TABLE_STRIPED); // init metadata cache docMetaDataCache = new HashMap<>(); addStyleName("docvis-table"); this.docVisualizerConfig = docBrowserPanel.getDocBrowserConfig(); } public Button generateInfoButtonCell(final String docName) { Button btn = new Button(); btn.setStyleName(ChameleonTheme.BUTTON_BORDERLESS); btn.setIcon(INFO_ICON); btn.addClickListener(new Button.ClickListener() { @Override public void buttonClick(Button.ClickEvent event) { try { List<Annotation> annos = getDocMetaData(docName); /** * Transforms to a list of key value pairs. The values concates the * namespace and ordinary value. Namespaces "NULL" are ignored. */ // create datasource and bind it to a table BeanItemContainer<Annotation> metaContainer = new BeanItemContainer<>(Annotation.class); metaContainer.addAll(annos); metaContainer.sort(new Object[] { "namespace", "name" }, new boolean[] { true, true }); Table metaTable = new Table(); metaTable.setContainerDataSource(metaContainer); metaTable.addGeneratedColumn("genname", new MetaDataPanel.MetaTableNameGenerator(metaContainer)); metaTable.addGeneratedColumn("genvalue", new MetaDataPanel.MetaTableValueGenerator(metaContainer)); metaTable.setVisibleColumns("genname", "genvalue"); metaTable.setColumnHeaders(new String[] { "Name", "Value" }); metaTable.setSizeFull(); metaTable.setColumnWidth("genname", -1); metaTable.setColumnExpandRatio("genvalue", 1.0f); metaTable.addStyleName(ChameleonTheme.TABLE_STRIPED); // create and style the extra window for the metadata table Window metaWin = new Window(); metaWin.setContent(metaTable); metaWin.setCaption("metadata doc " + docName); metaWin.center(); metaWin.setWidth(400, Unit.PIXELS); metaWin.setHeight(400, Unit.PIXELS); // paint the window docBrowserPanel.getUI().addWindow(metaWin); } catch (UniformInterfaceException ex) { log.error("can not retrieve metadata for document " + docName, ex); } } }); return btn; } /** * Sort the table by a given config. The config includes metadata keys and the * table is sorted lexicographically by their values. If not config for * sorting is determined the document name is used for sorting. */ private void sortByMetaData(List<MetaDataCol> sortColumns) { if (sortColumns == null || sortColumns.isEmpty()) { sort(new Object[] { "document name" }, new boolean[] { true }); return; } Object[] sortByColumns = new Object[sortColumns.size()]; boolean[] ascendingOrDescending = new boolean[sortColumns.size()]; for (int i = 0; i < sortColumns.size(); i++) { sortByColumns[i] = sortColumns.get(i).getColName(); ascendingOrDescending[i] = sortColumns.get(i).ascending; } sort(sortByColumns, ascendingOrDescending); } private Panel generateVisualizerLinks(String docName) { Panel p = new Panel(); VerticalLayout l = new VerticalLayout(); p.addStyleName(ChameleonTheme.PANEL_BORDERLESS); if (docVisualizerConfig != null) { Visualizer[] visualizers = docVisualizerConfig.getVisualizers(); if (visualizers != null) { for (Visualizer visualizer : visualizers) { Button openVis = new Button(visualizer.getDisplayName()); openVis.setDescription("open visualizer with the full text of " + docName); openVis.addClickListener(new OpenVisualizerWindow(docName, visualizer, openVis)); openVis.setStyleName(BaseTheme.BUTTON_LINK); openVis.setDisableOnClick(true); l.addComponent(openVis); } } } p.setContent(l); return p; } public static DocBrowserTable getDocBrowserTable(DocBrowserPanel parent) { DocBrowserTable docBrowserTable = new DocBrowserTable(parent); return docBrowserTable; } private class OpenVisualizerWindow implements Button.ClickListener { private String docName; private Visualizer config; private final Button button; public OpenVisualizerWindow(String docName, Visualizer config, Button btn) { this.button = btn; this.docName = docName; this.config = config; } @Override public void buttonClick(Button.ClickEvent event) { docBrowserPanel.openVis(docName, config, button); } } /** * Retrieves date from the cache or from the annis rest service for a specific * document. * * @param document The document the data are fetched for. * @return The a list of meta data. Can be empty but never null. */ private List<Annotation> getDocMetaData(String document) { // lookup up meta data in the cache if (!docMetaDataCache.containsKey(docBrowserPanel.getCorpus())) { // get the metadata for the corpus WebResource res = Helper.getAnnisWebResource(); res = res.path("meta/corpus/").path(urlPathEscape.escape(docBrowserPanel.getCorpus())).path("closure"); Map<String, List<Annotation>> metaDataMap = new HashMap<>(); // create a document -> metadata map for (Annotation a : res.get(new Helper.AnnotationListType())) { if (a.getAnnotationPath() != null && !a.getAnnotationPath().isEmpty() && a.getType().equals("DOCUMENT")) { String docName = a.getAnnotationPath().get(0); if (!metaDataMap.containsKey(docName)) { metaDataMap.put(docName, new ArrayList<Annotation>()); } metaDataMap.get(docName).add(a); } } docMetaDataCache.put(docBrowserPanel.getCorpus(), metaDataMap); } if (docMetaDataCache.get(docBrowserPanel.getCorpus()).containsKey(document)) { return docMetaDataCache.get(docBrowserPanel.getCorpus()).get(document); } else { return new ArrayList<Annotation>(); } } private String generateCell(List<String> path, MetaDataCol metaDatum) { List<Annotation> metaData = new LinkedList<>(); if (path != null && !path.isEmpty()) { metaData = getDocMetaData(path.get(path.size() - 1)); } // lookup meta data for (Annotation a : metaData) { if (metaDatum.namespace != null && metaDatum.namespace.equals(a.getNamespace()) && metaDatum.name.equals(a.getName())) { return a.getValue(); } if (metaDatum.namespace == null && a.getNamespace() == null && metaDatum.name.equals(a.getName())) { return a.getValue(); } } return "n/a"; } private static class MetaColumns { List<MetaDataCol> visibleColumns; List<MetaDataCol> sortColumns; public MetaColumns() { this.visibleColumns = new ArrayList<>(); this.sortColumns = new ArrayList<>(); } } private static class MetaDataCol { String namespace; String name; boolean ascending; public MetaDataCol(String namespace, String name) { this.namespace = namespace; this.name = name; } public MetaDataCol(String namespace, String name, boolean ascending) { this(namespace, name); this.ascending = ascending; } String getColName() { return namespace != null && !namespace.equalsIgnoreCase("null") ? namespace + ":" + name : name; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final MetaDataCol other = (MetaDataCol) obj; if ((this.namespace == null) ? (other.namespace != null) : !this.namespace.equals(other.namespace)) { return false; } if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 97 * hash + (this.namespace != null ? this.namespace.hashCode() : 0); hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0); return hash; } } }