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; import annis.gui.components.HelpButton; import annis.gui.controlpanel.QueryPanel; import annis.gui.controlpanel.SearchOptionsPanel; import annis.gui.converter.CommaSeperatedStringConverterList; import annis.gui.exporter.Exporter; import annis.gui.objects.QueryUIState; import com.google.common.base.Stopwatch; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; import com.vaadin.data.Property; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.validator.IntegerRangeValidator; import com.vaadin.server.FileDownloader; import com.vaadin.server.FileResource; import com.vaadin.server.FontAwesome; import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.ComboBox; import com.vaadin.ui.FormLayout; import com.vaadin.ui.GridLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Notification; import com.vaadin.ui.ProgressBar; import com.vaadin.ui.TextField; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; import java.io.File; import java.util.HashMap; import java.util.Map; import org.slf4j.LoggerFactory; /** * * @author Thomas Krause <krauseto@hu-berlin.de> */ public class ExportPanel extends GridLayout { private static final org.slf4j.Logger log = LoggerFactory.getLogger(ExportPanel.class); private final ComboBox cbLeftContext; private final ComboBox cbRightContext; private final TextField txtAnnotationKeys; private final TextField txtParameters; private final Map<String, String> help4Exporter = new HashMap<>(); private final ComboBox cbExporter; private final Button btDownload; private final Button btExport; private final Button btCancel; private final QueryPanel queryPanel; private File tmpOutputFile; private final ProgressBar progressBar; private final Label progressLabel; private FileDownloader downloader; private final transient EventBus eventBus; private transient Stopwatch exportTime = Stopwatch.createUnstarted(); private final QueryController controller; private UI ui; private final QueryUIState state; private final FormLayout formLayout; private final Label lblHelp; public ExportPanel(QueryPanel queryPanel, QueryController controller, QueryUIState state) { super(2, 3); this.queryPanel = queryPanel; this.controller = controller; this.state = state; this.eventBus = new EventBus(); this.eventBus.register(ExportPanel.this); this.formLayout = new FormLayout(); formLayout.setWidth("-1px"); setWidth("99%"); setHeight("-1px"); initHelpMessages(); setColumnExpandRatio(0, 0.0f); setColumnExpandRatio(1, 1.0f); cbExporter = new ComboBox("Exporter"); cbExporter.setNewItemsAllowed(false); cbExporter.setNullSelectionAllowed(false); cbExporter.setImmediate(true); for (Exporter e : SearchView.EXPORTER) { cbExporter.addItem(e.getClass().getSimpleName()); } cbExporter.setValue(SearchView.EXPORTER[0].getClass().getSimpleName()); cbExporter.addValueChangeListener(new ExporterSelectionHelpListener()); formLayout.addComponent(cbExporter); addComponent(formLayout, 0, 0); lblHelp = new Label(help4Exporter.get((String) cbExporter.getValue())); lblHelp.setContentMode(ContentMode.HTML); addComponent(lblHelp, 1, 0); cbLeftContext = new ComboBox("Left Context"); cbRightContext = new ComboBox("Right Context"); cbLeftContext.setNullSelectionAllowed(false); cbRightContext.setNullSelectionAllowed(false); cbLeftContext.setNewItemsAllowed(true); cbRightContext.setNewItemsAllowed(true); cbLeftContext .addValidator(new IntegerRangeValidator("must be a number", Integer.MIN_VALUE, Integer.MAX_VALUE)); cbRightContext .addValidator(new IntegerRangeValidator("must be a number", Integer.MIN_VALUE, Integer.MAX_VALUE)); for (Integer i : SearchOptionsPanel.PREDEFINED_CONTEXTS) { cbLeftContext.addItem(i); cbRightContext.addItem(i); } cbLeftContext.setValue(5); cbRightContext.setValue(5); formLayout.addComponent(cbLeftContext); formLayout.addComponent(cbRightContext); txtAnnotationKeys = new TextField("Annotation Keys"); txtAnnotationKeys.setDescription("Some exporters will use this comma " + "seperated list of annotation keys to limit the exported data to these " + "annotations."); formLayout.addComponent(new HelpButton(txtAnnotationKeys)); txtParameters = new TextField("Parameters"); txtParameters.setDescription( "You can input special parameters " + "for certain exporters. See the description of each exporter " + "(? button above) for specific parameter settings."); formLayout.addComponent(new HelpButton(txtParameters)); btExport = new Button("Perform Export"); btExport.setIcon(FontAwesome.PLAY); btExport.setDisableOnClick(true); btExport.addClickListener(new ExportButtonListener()); btCancel = new Button("Cancel Export"); btCancel.setIcon(FontAwesome.TIMES_CIRCLE); btCancel.setEnabled(false); btCancel.addClickListener(new CancelButtonListener()); btCancel.setVisible(SearchView.EXPORTER[0].isCancelable()); btDownload = new Button("Download"); btDownload.setDescription("Click here to start the actual download."); btDownload.setIcon(FontAwesome.DOWNLOAD); btDownload.setDisableOnClick(true); btDownload.setEnabled(false); HorizontalLayout layoutExportButtons = new HorizontalLayout(btExport, btCancel, btDownload); addComponent(layoutExportButtons, 0, 1, 1, 1); VerticalLayout vLayout = new VerticalLayout(); addComponent(vLayout, 0, 2, 1, 2); progressBar = new ProgressBar(); progressBar.setVisible(false); progressBar.setIndeterminate(true); vLayout.addComponent(progressBar); progressLabel = new Label(); vLayout.addComponent(progressLabel); if (state != null) { cbLeftContext.setPropertyDataSource(state.getLeftContext()); cbRightContext.setPropertyDataSource(state.getRightContext()); cbExporter.setPropertyDataSource(state.getExporterName()); state.getExporterName().setValue(SearchView.EXPORTER[0].getClass().getSimpleName()); txtAnnotationKeys.setConverter(new CommaSeperatedStringConverterList()); txtAnnotationKeys.setPropertyDataSource(state.getExportAnnotationKeys()); txtParameters.setPropertyDataSource(state.getExportParameters()); } } @Override public void attach() { super.attach(); this.ui = UI.getCurrent(); } private void initHelpMessages() { help4Exporter.put(SearchView.EXPORTER[0].getClass().getSimpleName(), "The WEKA Exporter exports only the " + "values of the elements searched for by the user, ignoring the context " + "around search results. The values for all annotations of each of the " + "found nodes is given in a comma-separated table (CSV). At the top of " + "the export, the names of the columns are given in order according to " + "the WEKA format.<br/><br/>" + "Parameters: <br/>" + "<em>metakeys</em> - comma seperated list of all meta data to include in the result (e.g. " + "<code>metakeys=title,documentname</code>)"); help4Exporter.put(SearchView.EXPORTER[1].getClass().getSimpleName(), "The CSV Exporter exports only the " + "values of the elements searched for by the user, ignoring the context " + "around search results. The values for all annotations of each of the " + "found nodes is given in a comma-separated table (CSV). <br/><br/>" + "Parameters: <br/>" + "<em>metakeys</em> - comma seperated list of all meta data to include in the result (e.g. " + "<code>metakeys=title,documentname</code>)"); help4Exporter.put(SearchView.EXPORTER[2].getClass().getSimpleName(), "The Token Exporter exports the token covered by the matched nodes of every search result and " + "its context, one line per result. " + "Beside the text of the token it also contains all token annotations separated by \"/\"." + "<p>" + "<strong>This exporter does not work well with dialog data " + "(corpora that have more than one primary text). " + "Use the GridExporter instead.</strong>" + "</p>"); help4Exporter.put(SearchView.EXPORTER[3].getClass().getSimpleName(), "The Grid Exporter can export all annotations of a search result and its " + "context. Each annotation layer is represented in a separate line, and the " + "tokens covered by each annotation are given as number ranges after each " + "annotation in brackets. To suppress token numbers, input numbers=false " + "into the parameters box below. To display only a subset of annotations " + "in any order use the \"Annotation keys\" text field, input e.g. \"tok,pos,cat\" " + "to show tokens and the " + "annotations pos and cat.<br /><br />" + "Parameters: <br/>" + "<em>metakeys</em> - comma seperated list of all meta data to include in the result (e.g. " + "<code>metakeys=title,documentname</code>) <br />" + "<em>numbers</em> - set to \"false\" if the grid event numbers should not be included in the output (e.g. " + "<code>numbers=false</code>)"); help4Exporter.put(SearchView.EXPORTER[4].getClass().getSimpleName(), "The SimpleTextExporter exports only the plain text of every search result. " + "<p>" + "<strong>This exporter does not work well with dialog data " + "(corpora that have more than one primary text). " + "Use the GridExporter instead.</strong>" + "</p>"); } public class ExporterSelectionHelpListener implements Property.ValueChangeListener { @Override public void valueChange(ValueChangeEvent event) { String helpMessage = help4Exporter.get((String) event.getProperty().getValue()); if (helpMessage != null) { lblHelp.setValue(helpMessage); } else { lblHelp.setValue("No help available for this exporter"); } Exporter exporter = controller.getExporterByName((String) event.getProperty().getValue()); if (exporter != null) { btCancel.setVisible(exporter.isCancelable()); } } } @Subscribe public void handleExportProgress(final Integer exports) { if (ui != null) { // if we ui access() here it seems to confuse the isInterrupted() flag // of the parent thread and cancelling won't work any longer ui.accessSynchronously(new Runnable() { @Override public void run() { if (exportTime != null && exportTime.isRunning()) { progressLabel.setValue("exported " + exports + " items in " + exportTime.toString()); } else { progressLabel.setValue("exported " + exports + " items"); } } }); } } @Override public void detach() { super.detach(); if (tmpOutputFile != null && tmpOutputFile.exists()) { if (!tmpOutputFile.delete()) { log.warn("Could not delete {}", tmpOutputFile.getAbsolutePath()); } } } public void showResult(File currentTmpFile, boolean manuallyCancelled) { btExport.setEnabled(true); btCancel.setEnabled(false); progressBar.setVisible(false); progressLabel.setValue(""); // copy the result to the class member in order to delete if // when not longer needed tmpOutputFile = currentTmpFile; if (tmpOutputFile == null) { Notification.show("Could not create the Exporter", "The server logs might contain more information about this " + "so you should contact the provider of this ANNIS installation " + "for help.", Notification.Type.ERROR_MESSAGE); } else if (manuallyCancelled) { // we were aborted, don't do anything Notification.show("Export cancelled", Notification.Type.WARNING_MESSAGE); } else { if (downloader != null && btDownload.getExtensions().contains(downloader)) { btDownload.removeExtension(downloader); } downloader = new FileDownloader(new FileResource(tmpOutputFile)); downloader.extend(btDownload); btDownload.setEnabled(true); Notification.show("Export finished", "Click on the button right to the export button to actually download the file.", Notification.Type.HUMANIZED_MESSAGE); } } private class ExportButtonListener implements Button.ClickListener { @Override public void buttonClick(ClickEvent event) { // clean up old export if (tmpOutputFile != null && tmpOutputFile.exists()) { if (!tmpOutputFile.delete()) { log.warn("Could not delete {}", tmpOutputFile.getAbsolutePath()); } } tmpOutputFile = null; String exporterName = (String) cbExporter.getValue(); final Exporter exporter = controller.getExporterByName(exporterName); if (exporter != null) { if ("".equals(queryPanel.getQuery())) { Notification.show("Empty query", Notification.Type.WARNING_MESSAGE); btExport.setEnabled(true); return; } else if (state.getSelectedCorpora().getValue().isEmpty()) { Notification.show("Please select a corpus", Notification.Type.WARNING_MESSAGE); btExport.setEnabled(true); return; } btDownload.setEnabled(false); progressBar.setVisible(true); progressLabel.setValue(""); if (exporter.isCancelable()) { btCancel.setEnabled(true); btCancel.setDisableOnClick(true); } controller.executeExport(ExportPanel.this, eventBus); if (exportTime == null) { exportTime = Stopwatch.createUnstarted(); } exportTime.reset(); exportTime.start(); } } } private class CancelButtonListener implements Button.ClickListener { @Override public void buttonClick(ClickEvent event) { controller.cancelExport(); } } }