annis.gui.ExportPanel.java Source code

Java tutorial

Introduction

Here is the source code for annis.gui.ExportPanel.java

Source

/*
 * 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();
        }

    }

}