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

weka.gui.beans.LogPanel Maven / Gradle / Ivy

/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see .
 */

/*
 *    LogPanel
 *    Copyright (C) 2008-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.gui.beans;

import weka.gui.Logger;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Iterator;

/**
 * Class for displaying a status area (made up of a variable number of lines)
 * and a log area.
 * 
 * @author mhall (mhall{[at]}pentaho{[dot]}com)
 * @version $Revision: 12361 $
 */
public class LogPanel extends JPanel implements Logger {

  /** Added ID to avoid warning */
  private static final long serialVersionUID = 6583097154513435548L;

  /**
   * Holds the index (line number) in the JTable of each component being
   * tracked.
   */
  protected HashMap m_tableIndexes =
    new HashMap();

  /**
   * Holds the timers associated with each component being tracked.
   */
  private final HashMap m_timers = new HashMap();

  /**
   * The table model for the JTable used in the status area
   */
  private final DefaultTableModel m_tableModel;

  /**
   * The table for the status area
   */
  private JTable m_table;

  /**
   * Tabbed pane to hold both the status and the log
   */
  private final JTabbedPane m_tabs = new JTabbedPane();

  /**
   * For formatting timer digits
   */
  private final DecimalFormat m_formatter = new DecimalFormat("00");

  /**
   * The log panel to delegate log messages to.
   */
  private final weka.gui.LogPanel m_logPanel = new weka.gui.LogPanel(null,
    false, true, false);

  public LogPanel() {

    String[] columnNames = { "Component", "Parameters", "Time", "Status" };
    m_tableModel = new DefaultTableModel(columnNames, 0);

    // JTable with error/warning indication for rows.
    m_table = new JTable() {

      /** Added ID to avoid warning */
      private static final long serialVersionUID = 5883722364387855125L;

      @Override
      public Class getColumnClass(int column) {
        return getValueAt(0, column).getClass();
      }

      @Override
      public Component prepareRenderer(TableCellRenderer renderer, int row,
        int column) {
        Component c = super.prepareRenderer(renderer, row, column);
        if (!c.getBackground().equals(getSelectionBackground())) {
          String type = (String) getModel().getValueAt(row, 3);
          Color backgroundIndicator = null;
          if (type.startsWith("ERROR")) {
            backgroundIndicator = Color.RED;
          } else if (type.startsWith("WARNING")) {
            backgroundIndicator = Color.YELLOW;
          } else if (type.startsWith("INTERRUPTED")) {
            backgroundIndicator = Color.MAGENTA;
          }
          c.setBackground(backgroundIndicator);
        }
        return c;
      }
    };

    m_table.setModel(m_tableModel);
    m_table.getColumnModel().getColumn(0).setPreferredWidth(100);
    m_table.getColumnModel().getColumn(1).setPreferredWidth(150);
    m_table.getColumnModel().getColumn(2).setPreferredWidth(40);
    m_table.getColumnModel().getColumn(3).setPreferredWidth(350);
    m_table.setShowVerticalLines(true);

    JPanel statusPan = new JPanel();
    statusPan.setLayout(new BorderLayout());
    statusPan.add(new JScrollPane(m_table), BorderLayout.CENTER);
    m_tabs.addTab("Status", statusPan);
    m_tabs.addTab("Log", m_logPanel);

    setLayout(new BorderLayout());
    add(m_tabs, BorderLayout.CENTER);

  }

  /**
   * Clear the status area.
   */
  public void clearStatus() {
    // stop any running timers
    Iterator i = m_timers.values().iterator();
    while (i.hasNext()) {
      i.next().stop();
    }

    // clear the map entries
    m_timers.clear();
    m_tableIndexes.clear();

    // clear the rows from the table
    while (m_tableModel.getRowCount() > 0) {
      m_tableModel.removeRow(0);
    }
  }

  /**
   * The JTable used for the status messages (in case clients want to attach
   * listeners etc.)
   * 
   * @return the JTable used for the status messages.
   */
  public JTable getStatusTable() {
    return m_table;
  }

  /**
   * Sends the supplied message to the log area. These message will typically
   * have the current timestamp prepended, and be viewable as a history.
   * 
   * @param message the log message
   */
  @Override
  public synchronized void logMessage(String message) {
    // delegate to the weka.gui.LogPanel
    m_logPanel.logMessage(message);
  }

  /**
   * Sends the supplied message to the status area. These messages are typically
   * one-line status messages to inform the user of progress during processing
   * (i.e. it doesn't matter if the user doesn't happen to look at each
   * message). These messages have the following format:
   * 
   * |
   * 
   * @param message the status message.
   */
  @Override
  public synchronized void statusMessage(String message) {

    boolean hasDelimiters = (message.indexOf('|') > 0);
    String stepName = "";
    String stepHash = "";
    String stepParameters = "";
    String stepStatus = "";
    boolean noTimer = false;

    if (!hasDelimiters) {
      stepName = "Unknown";
      stepHash = "Unknown";
      stepStatus = message;
    } else {
      // Extract the fields of the status message
      stepHash = message.substring(0, message.indexOf('|'));
      message = message.substring(message.indexOf('|') + 1, message.length());
      // See if there is a unique object ID in the stepHash string
      if (stepHash.indexOf('$') > 0) {
        // Extract the step name
        stepName = stepHash.substring(0, stepHash.indexOf('$'));
      } else {
        stepName = stepHash;
      }

      if (stepName.startsWith("@!@")) {
        noTimer = true;
        stepName = stepName.substring(3, stepName.length());
      }

      // See if there are any step parameters to extract
      if (message.indexOf('|') >= 0) {
        stepParameters = message.substring(0, message.indexOf('|'));
        stepStatus = message.substring(message.indexOf('|') + 1,
          message.length());
      } else {
        // set the status message to the remainder
        stepStatus = message;
      }
    }

    // Now see if this step is in the hashmap
    if (m_tableIndexes.containsKey(stepHash)) {
      // Get the row number and update the table model...
      final Integer rowNum = m_tableIndexes.get(stepHash);
      if (stepStatus.equalsIgnoreCase("remove")
        || stepStatus.equalsIgnoreCase("remove.")) {

        // m_tableModel.fireTableDataChanged();
        m_tableIndexes.remove(stepHash);
        Timer t = m_timers.get(stepHash);
        if (t != null) {
          t.stop();
          m_timers.remove(stepHash);
        }

        // now need to decrement all the row indexes of
        // any rows greater than this one
        Iterator i = m_tableIndexes.keySet().iterator();
        while (i.hasNext()) {
          String nextKey = i.next();
          int index = m_tableIndexes.get(nextKey).intValue();
          if (index > rowNum.intValue()) {
            index--;
            // System.err.println("*** " + nextKey + " needs decrementing to " +
            // index);
            m_tableIndexes.put(nextKey, index);
            // System.err.println("new index " +
            // m_rows.get(nextKey).intValue());
          }
        }

        // Remove the entry...
        if (!SwingUtilities.isEventDispatchThread()) {
          try {
            SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                m_tableModel.removeRow(rowNum);
              }
            });
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        } else {
          m_tableModel.removeRow(rowNum);
        }
      } else {
        final String stepNameCopy = stepName;
        final String stepStatusCopy = stepStatus;
        final String stepParametersCopy = stepParameters;

        if (!SwingUtilities.isEventDispatchThread()) {
          try {
            SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                String currentStatus = m_tableModel.getValueAt(rowNum.intValue(), 3).toString();
                if (currentStatus.startsWith("INTERRUPTED") || currentStatus.startsWith("ERROR")) {
                  // leave these in place until the status area is cleared.
                  return;
                }
                // ERROR overrides INTERRUPTED
                if (!(stepStatusCopy.startsWith("INTERRUPTED") && ((String) m_tableModel
                  .getValueAt(rowNum.intValue(), 3)).startsWith("ERROR"))) {
                  m_tableModel.setValueAt(stepNameCopy, rowNum.intValue(), 0);
                  m_tableModel.setValueAt(stepParametersCopy,
                    rowNum.intValue(), 1);
                  m_tableModel.setValueAt(
                    m_table.getValueAt(rowNum.intValue(), 2),
                    rowNum.intValue(), 2);
                  m_tableModel.setValueAt(stepStatusCopy, rowNum.intValue(), 3);
                }
              }
            });
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        } else {
          String currentStatus = m_tableModel.getValueAt(rowNum.intValue(), 3).toString();
          if (currentStatus.startsWith("INTERRUPTED") || currentStatus.startsWith("ERROR")) {
            // leave these in place until the status area is cleared.
            return;
          }
          if (!(stepStatusCopy.startsWith("INTERRUPTED") && ((String) m_tableModel
            .getValueAt(rowNum.intValue(), 3)).startsWith("ERROR"))) {
            m_tableModel.setValueAt(stepNameCopy, rowNum.intValue(), 0);
            m_tableModel.setValueAt(stepParametersCopy, rowNum.intValue(), 1);
            m_tableModel.setValueAt(m_table.getValueAt(rowNum.intValue(), 2),
              rowNum.intValue(), 2);
            if (m_table.getValueAt(rowNum.intValue(), 3) != null
              && !m_table.getValueAt(rowNum.intValue(), 3).toString()
                .toLowerCase()
                .startsWith("finished")) {
              m_tableModel.setValueAt(stepStatusCopy, rowNum.intValue(), 3);
            }
          }
        }
        if (stepStatus.startsWith("ERROR")
          || stepStatus.startsWith("INTERRUPTED")
          || stepStatus.toLowerCase().startsWith("finished")
          ||
          // stepStatus.toLowerCase().startsWith("finished.") ||
          stepStatus.toLowerCase().startsWith("done")
          ||
          // stepStatus.toLowerCase().startsWith("done.") ||
          stepStatus.equalsIgnoreCase("stopped")
          || stepStatus.equalsIgnoreCase("stopped.")) {
          // stop the timer.
          Timer t = m_timers.get(stepHash);
          if (t != null) {
            t.stop();
          }
        } else if (m_timers.get(stepHash) != null
          && !m_timers.get(stepHash).isRunning()) {
          // need to create a new one in order to reset the
          // elapsed time.
          installTimer(stepHash);
        }
        // m_tableModel.fireTableCellUpdated(rowNum.intValue(), 3);
      }
    } else if (!stepStatus.equalsIgnoreCase("Remove")
      && !stepStatus.equalsIgnoreCase("Remove.")) {
      // Add this one to the hash map
      int numKeys = m_tableIndexes.keySet().size();
      m_tableIndexes.put(stepHash, numKeys);

      // Now add a row to the table model
      final Object[] newRow = new Object[4];
      newRow[0] = stepName;
      newRow[1] = stepParameters;
      newRow[2] = "-";
      newRow[3] = stepStatus;
      final String stepHashCopy = stepHash;
      try {
        if (!SwingUtilities.isEventDispatchThread()) {
          SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
              m_tableModel.addRow(newRow);
              // m_tableModel.fireTableDataChanged();
            }
          });
        } else {
          m_tableModel.addRow(newRow);
        }

        if (!noTimer && !stepStatus.toLowerCase().startsWith("finished")
          && !stepStatus.toLowerCase().startsWith("done")) {
          installTimer(stepHashCopy);
        }
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }

  private void installTimer(final String stepHash) {
    final long startTime = System.currentTimeMillis();
    Timer newTimer = new Timer(1000, new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        synchronized (LogPanel.this) {
          if (m_tableIndexes.containsKey(stepHash)) {
            final Integer rn = m_tableIndexes.get(stepHash);
            long elapsed = System.currentTimeMillis() - startTime;
            long seconds = elapsed / 1000;
            long minutes = seconds / 60;
            final long hours = minutes / 60;
            seconds = seconds - (minutes * 60);
            minutes = minutes - (hours * 60);
            final long seconds2 = seconds;
            final long minutes2 = minutes;
            if (!SwingUtilities.isEventDispatchThread()) {
              try {
                SwingUtilities.invokeLater(new Runnable() {
                  @Override
                  public void run() {
                    m_tableModel.setValueAt(
                      "" + m_formatter.format(hours) + ":"
                        + m_formatter.format(minutes2) + ":"
                        + m_formatter.format(seconds2), rn.intValue(), 2);
                  }
                });
              } catch (Exception ex) {
                ex.printStackTrace();
              }
            } else {
              m_tableModel.setValueAt(
                "" + m_formatter.format(hours) + ":"
                  + m_formatter.format(minutes2) + ":"
                  + m_formatter.format(seconds2), rn.intValue(), 2);
            }
          }
        }
      }
    });
    m_timers.put(stepHash, newTimer);
    newTimer.start();
  }

  /**
   * Set the size of the font used in the log message area. <= 0 will use the
   * default for JTextArea.
   *
   * @param size the size of the font to use in the log message area
   */
  public void setLoggingFontSize(int size) {
    m_logPanel.setLoggingFontSize(size);
  }

  /**
   * Main method to test this class.
   * 
   * @param args any arguments (unused)
   */
  public static void main(String[] args) {
    try {
      final javax.swing.JFrame jf = new javax.swing.JFrame("Status/Log Panel");

      jf.getContentPane().setLayout(new BorderLayout());
      final LogPanel lp = new LogPanel();
      jf.getContentPane().add(lp, BorderLayout.CENTER);

      jf.getContentPane().add(lp, BorderLayout.CENTER);
      jf.addWindowListener(new java.awt.event.WindowAdapter() {
        @Override
        public void windowClosing(java.awt.event.WindowEvent e) {
          jf.dispose();
          System.exit(0);
        }
      });
      jf.pack();
      jf.setVisible(true);
      lp.statusMessage("Step 1|Some options here|A status message");
      lp.statusMessage("Step 2$hashkey|Status message: no options");
      Thread.sleep(3000);
      lp.statusMessage("Step 2$hashkey|Funky Chickens!!!");
      Thread.sleep(3000);
      lp.statusMessage("Step 1|Some options here|finished");
      // lp.statusMessage("Step 1|Some options here|back again!");
      Thread.sleep(3000);
      lp.statusMessage("Step 2$hashkey|ERROR! More Funky Chickens!!!");
      Thread.sleep(3000);
      lp.statusMessage("Step 2$hashkey|WARNING - now a warning...");
      Thread.sleep(3000);
      lp.statusMessage("Step 2$hashkey|Back to normal.");
      Thread.sleep(3000);
      lp.statusMessage("Step 2$hashkey|INTERRUPTED.");

    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy