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

org.datacleaner.panels.result.ProgressInformationPanel Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Free Software Foundation, Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.panels.result;

import java.awt.BorderLayout;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

import javax.swing.JTextArea;
import javax.swing.border.MatteBorder;

import org.apache.metamodel.schema.Table;
import org.datacleaner.api.RestrictedFunctionalityCallToAction;
import org.datacleaner.api.RestrictedFunctionalityException;
import org.datacleaner.panels.DCPanel;
import org.datacleaner.util.ErrorUtils;
import org.datacleaner.util.IconUtils;
import org.datacleaner.util.ProgressCounter;
import org.datacleaner.util.WidgetFactory;
import org.datacleaner.util.WidgetUtils;
import org.datacleaner.widgets.DCTaskPaneContainer;
import org.datacleaner.windows.ResultWindow;
import org.jdesktop.swingx.JXTaskPane;
import org.jdesktop.swingx.JXTitledPanel;
import org.jdesktop.swingx.VerticalLayout;
import org.joda.time.LocalTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

import com.google.common.base.Stopwatch;
import com.google.common.base.Strings;

/**
 * Panel that shows various progress information widgets within the
 * {@link ResultWindow}.
 */
public class ProgressInformationPanel extends DCPanel {

    private static final long serialVersionUID = 1L;

    private static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormat.forPattern("HH:mm:ss");
    private static final int MARGIN = 10;

    private final JTextArea _executionLogTextArea;
    private final DCPanel _progressBarPanel;
    private final ConcurrentMap _tableProgressInformationPanels;
    private final ConcurrentMap _progressTimingCounters;
    private final Stopwatch _stopWatch;

    public ProgressInformationPanel(final boolean running) {
        super(WidgetUtils.COLOR_DEFAULT_BACKGROUND);
        setLayout(new BorderLayout());
        _tableProgressInformationPanels = new ConcurrentHashMap<>();
        _progressTimingCounters = new ConcurrentHashMap<>();
        _stopWatch = Stopwatch.createUnstarted();
        _executionLogTextArea = new JTextArea();
        _executionLogTextArea.setText("--- DataCleaner progress information user-log ---");
        _executionLogTextArea.setEditable(false);
        _executionLogTextArea.setBackground(WidgetUtils.COLOR_DEFAULT_BACKGROUND);

        _progressBarPanel = new DCPanel(WidgetUtils.COLOR_ALTERNATIVE_BACKGROUND);
        _progressBarPanel.setLayout(new VerticalLayout(4));

        final JXTaskPane progressTaskPane = WidgetFactory.createTaskPane("Progress", IconUtils.ACTION_EXECUTE);
        progressTaskPane.add(_progressBarPanel);

        final JXTitledPanel executionLogPanel =
                WidgetFactory.createTitledPanel("Execution log", WidgetUtils.scrolleable(_executionLogTextArea));
        executionLogPanel.setBorder(new MatteBorder(1, 1, 1, 1, WidgetUtils.COLOR_ALTERNATIVE_BACKGROUND));
        final DCTaskPaneContainer taskPaneContainer = WidgetFactory.createTaskPaneContainer();
        taskPaneContainer.setLayout(new BorderLayout(MARGIN, MARGIN));

        if (running) {
            taskPaneContainer.add(progressTaskPane, BorderLayout.NORTH);
        }

        setBorder(new MatteBorder(0, 0, MARGIN, 0, WidgetUtils.COLOR_DEFAULT_BACKGROUND));
        taskPaneContainer.add(executionLogPanel, BorderLayout.CENTER);
        add(taskPaneContainer, BorderLayout.CENTER);
    }

    public String getTextAreaText() {
        return _executionLogTextArea.getText();
    }

    private String getTimestamp() {
        return new LocalTime().toString(DATE_TIME_FORMAT);
    }

    public void addUserLog(final String string) {
        appendMessage("\n" + getTimestamp() + " INFO: " + string);
    }

    public void addUserLog(final String message, Throwable throwable, final boolean jobFinished) {
        final StringWriter stringWriter = new StringWriter();
        stringWriter.append("\n").append(getTimestamp());

        if (throwable instanceof RestrictedFunctionalityException) {
            stringWriter.append("RESTRICTED: ");
            stringWriter.append(message);
            appendMessage(stringWriter.toString());

            final RestrictedFunctionalityException restrictedFunctionalityException =
                    (RestrictedFunctionalityException) throwable;
            final String exceptionMessage = restrictedFunctionalityException.getMessage();
            final RestrictedFunctionalityCallToAction[] callToActions =
                    restrictedFunctionalityException.getCallToActions();
            addRestrictedFunctionalityMessage(exceptionMessage, callToActions);
            return;
        }

        stringWriter.append("ERROR: ");
        stringWriter.append(message);

        if (throwable == null) {
            stringWriter.append('\n');
            stringWriter.append("(No stack trace provided)");
        } else {
            throwable = ErrorUtils.unwrapForPresentation(throwable);

            final String exceptionMessage = throwable.getMessage();
            if (!Strings.isNullOrEmpty(exceptionMessage)) {
                stringWriter.append('\n');
                stringWriter.append('\n');
                stringWriter.append(exceptionMessage);
            }

            stringWriter.append('\n');
            stringWriter.append('\n');
            final PrintWriter printWriter = new PrintWriter(stringWriter);
            printStackTrace(printWriter, throwable);
            stringWriter.append('\n');
        }
        appendMessage(stringWriter.toString());

        if (jobFinished) {
            final Collection tableProgressInformationPanels =
                    _tableProgressInformationPanels.values();
            for (final TableProgressInformationPanel tableProgressInformationPanel : tableProgressInformationPanels) {
                tableProgressInformationPanel.setProgressStopped(throwable != null);
            }
        }
    }

    /**
     * Prints stacktraces to the string writer, and investigates the throwable
     * hierarchy to check if there's any {@link SQLException}s which also has
     * "next" exceptions.
     *
     * @param printWriter
     * @param throwable
     */
    protected void printStackTrace(final PrintWriter printWriter, final Throwable throwable) {
        throwable.printStackTrace(printWriter);
        Throwable cause = throwable.getCause();
        while (cause != null) {
            if (cause instanceof SQLException) {
                final SQLException nextException = ((SQLException) cause).getNextException();
                if (nextException != null) {
                    printWriter.print("Next exception: ");
                    printStackTrace(printWriter, nextException);
                }
            }
            cause = cause.getCause();
        }
    }

    private void appendMessage(final String message) {
        WidgetUtils.invokeSwingAction(() -> _executionLogTextArea.append(message));
    }

    public void addProgressBar(final Table table, final int expectedRows) {
        final TableProgressInformationPanel tableProgressInformationPanel =
                getTableProgressInformationPanel(table, expectedRows);
        WidgetUtils.invokeSwingAction(() -> {
            _progressBarPanel.add(tableProgressInformationPanel);
            tableProgressInformationPanel.setProgressMaximum(expectedRows);
            _progressBarPanel.updateUI();
        });
    }

    private TableProgressInformationPanel getTableProgressInformationPanel(final Table table, int expectedRows) {
        TableProgressInformationPanel tableProgressInformationPanel = _tableProgressInformationPanels.get(table);
        if (tableProgressInformationPanel == null) {
            if (expectedRows == -1) {
                expectedRows = Integer.MAX_VALUE;
            }
            tableProgressInformationPanel = new TableProgressInformationPanel(table, expectedRows);
            final TableProgressInformationPanel previous =
                    _tableProgressInformationPanels.putIfAbsent(table, tableProgressInformationPanel);
            if (previous != null) {
                tableProgressInformationPanel = previous;
            }
        }
        return tableProgressInformationPanel;
    }

    /**
     * Informs the panel that the progress for a table is updated
     *
     * @param table
     * @param currentRow
     */
    public void updateProgress(final Table table, final int currentRow) {
        final TableProgressInformationPanel tableProgressInformationPanel = getTableProgressInformationPanel(table, -1);
        final boolean greater = tableProgressInformationPanel.setProgress(currentRow);

        if (!greater) {
            // this may happen because of the multithreaded nature of the
            // execution - sometimes a notification can come in later than
            // previous notifications
            return;
        }

        ProgressCounter counter = _progressTimingCounters.get(table);
        if (counter == null) {
            counter = new ProgressCounter();
            final ProgressCounter previous = _progressTimingCounters.put(table, counter);
            if (previous != null) {
                counter = previous;
            }
        }

        final boolean log;
        final int previousCount = counter.get();
        if (currentRow - previousCount > 1000) {
            log = counter.setIfSignificantToUser(currentRow);
        } else {
            log = false;
        }
        if (log) {
            addUserLog("Progress of " + table.getName() + ": " + formatNumber(currentRow) + " rows processed");
        }
    }

    private String formatNumber(final int number) {
        final NumberFormat nf = NumberFormat.getInstance();
        return nf.format(number);
    }

    public void onCancelled() {
        appendMessage("\n--- DataCleaner job cancelled at " + getTimestamp() + " ---");
        final Collection tableProgressInformationPanels =
                _tableProgressInformationPanels.values();
        for (final TableProgressInformationPanel tableProgressInformationPanel : tableProgressInformationPanels) {
            tableProgressInformationPanel.setProgressCancelled();
        }
    }

    /**
     * Informs the panel that the progress for a table has finished.
     *
     * @param table
     */
    public void updateProgressFinished(final Table table) {
        final TableProgressInformationPanel tableProgressInformationPanel = getTableProgressInformationPanel(table, -1);
        tableProgressInformationPanel.setProgressFinished();
    }

    public void onSuccess() {
        if (_stopWatch.isRunning()) {
            _stopWatch.stop();
            _stopWatch.elapsed(TimeUnit.MINUTES);

            addUserLog("Job success! Elapsed time: " + _stopWatch);
        }

        final Collection tableProgressInformationPanels =
                _tableProgressInformationPanels.values();

        for (final TableProgressInformationPanel tableProgressInformationPanel : tableProgressInformationPanels) {
            tableProgressInformationPanel.setProgressFinished();
        }
    }

    public void onBegin() {
        addUserLog("Job begin");
        _stopWatch.start();
    }

    public void addRestrictedFunctionalityMessage(final String messageString,
            final RestrictedFunctionalityCallToAction[] callToActions) {
        final StringBuilder sb = new StringBuilder();
        sb.append('\n');
        sb.append('\n');
        sb.append(messageString);
        for (final RestrictedFunctionalityCallToAction callToAction : callToActions) {
            sb.append('\n');
            sb.append(" - " + callToAction.getName() + " - " + callToAction.getHref());
        }
        sb.append('\n');
        addUserLog(sb.toString());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy