source: src/main/java/weka/gui/beans/LogPanel.java @ 5

Last change on this file since 5 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 14.2 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or
5 *    (at your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful,
8 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 *    GNU General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
15 */
16
17/*
18 *    LogPanel
19 *    Copyright (C) 2008 University of Waikato, Hamilton, New Zealand
20 *
21 */
22
23package weka.gui.beans;
24
25import java.awt.BorderLayout;
26import java.awt.Color;
27import java.awt.Component;
28import java.awt.EventQueue;
29import java.awt.event.ActionEvent;
30import java.awt.event.ActionListener;
31import java.util.HashMap;
32import java.util.Iterator;
33
34import javax.swing.JPanel;
35import javax.swing.JScrollPane;
36import javax.swing.JTabbedPane;
37import javax.swing.JTable;
38import javax.swing.SwingUtilities;
39import javax.swing.Timer;
40import javax.swing.table.DefaultTableModel;
41import javax.swing.table.TableCellRenderer;
42
43import weka.gui.Logger;
44
45/**
46 * Class for displaying a status area (made up of a variable number of
47 * lines) and a log area.
48 *
49 * @author mhall (mhall{[at]}pentaho{[dot]}com)
50 * @version $Revision: 5563 $
51 */
52public class LogPanel extends JPanel implements Logger {
53 
54  /**
55   * Holds the index (line number) in the JTable of each component
56   * being tracked.
57   */
58  private HashMap<String,Integer> m_tableIndexes = 
59    new HashMap<String, Integer>();
60 
61  /**
62   * Holds the timers associated with each component being tracked.
63   */
64  private HashMap<String, Timer> m_timers =
65    new HashMap<String, Timer>();
66 
67  /**
68   * The table model for the JTable used in the status area
69   */
70  private final DefaultTableModel m_tableModel;
71 
72  /**
73   * The table for the status area
74   */
75  private JTable m_table;
76 
77  /**
78   * Tabbed pane to hold both the status and the log
79   */
80  private JTabbedPane m_tabs = new JTabbedPane();
81     
82  /**
83   * The log panel to delegate log messages to.
84   */
85  private weka.gui.LogPanel m_logPanel = 
86    new weka.gui.LogPanel(null, false, true, false);
87   
88  public LogPanel() {
89   
90    String[] columnNames = {"Component", "Parameters", "Time", "Status"};
91    m_tableModel = new DefaultTableModel(columnNames, 0);
92   
93    // JTable with error/warning indication for rows.
94    m_table = new JTable() {
95      public Class getColumnClass(int column) {
96        return getValueAt(0, column).getClass();
97      }
98
99      public Component prepareRenderer(TableCellRenderer renderer, 
100          int row, int column) {
101        Component c = super.prepareRenderer(renderer, row, column);
102        if (!c.getBackground().equals(getSelectionBackground()))
103        {
104          String type = (String)getModel().getValueAt(row, 3);
105          Color backgroundIndicator = null;
106          if (type.startsWith("ERROR")) {
107            backgroundIndicator = Color.RED;
108          } else if (type.startsWith("WARNING")) {
109            backgroundIndicator = Color.YELLOW;
110          } else if (type.startsWith("INTERRUPTED")) {
111            backgroundIndicator = Color.MAGENTA;
112          }
113          c.setBackground(backgroundIndicator);
114        }
115        return c;
116      }
117    };
118   
119    m_table.setModel(m_tableModel);
120    m_table.getColumnModel().getColumn(0).setPreferredWidth(100);
121    m_table.getColumnModel().getColumn(1).setPreferredWidth(150);
122    m_table.getColumnModel().getColumn(2).setPreferredWidth(2);
123    m_table.getColumnModel().getColumn(3).setPreferredWidth(500);
124    m_table.setShowVerticalLines(true);
125   
126    JPanel statusPan = new JPanel();
127    statusPan.setLayout(new BorderLayout());
128    statusPan.add(new JScrollPane(m_table), BorderLayout.CENTER);
129    m_tabs.addTab("Status", statusPan);
130    m_tabs.addTab("Log", m_logPanel);
131   
132    setLayout(new BorderLayout());
133    add(m_tabs, BorderLayout.CENTER);
134   
135  }
136 
137  /**
138   * Clear the status area.
139   */
140  public void clearStatus() {
141    // stop any running timers
142    Iterator<Timer> i = m_timers.values().iterator();
143    while (i.hasNext()) {
144      i.next().stop();
145    }
146   
147    // clear the map entries
148    m_timers.clear();
149    m_tableIndexes.clear();
150   
151    // clear the rows from the table
152    while (m_tableModel.getRowCount() > 0) {
153      m_tableModel.removeRow(0);
154    }
155  }
156 
157  /**
158   * The JTable used for the status messages (in case clients
159   * want to attach listeners etc.)
160   *
161   * @return the JTable used for the status messages.
162   */
163  public JTable getStatusTable() {
164    return m_table;
165  }
166 
167  /**
168   * Sends the supplied message to the log area. These message will typically
169   * have the current timestamp prepended, and be viewable as a history.
170   *
171   * @param message the log message
172   */
173  public synchronized void logMessage(String message) {
174    // delegate to the weka.gui.LogPanel
175    m_logPanel.logMessage(message);
176  }
177 
178  /**
179   * Sends the supplied message to the status area. These messages are
180   * typically one-line status messages to inform the user of progress
181   * during processing (i.e. it doesn't matter if the user doesn't happen
182   * to look at each message). These messages have the following format:
183   *
184   * <Component name (needs to be unique)>|<Parameter string (optional)|<Status message>
185   *
186   * @param message the status message.
187   */
188  public synchronized void statusMessage(String message) {
189
190    boolean hasDelimiters = (message.indexOf('|') > 0);
191    String stepName = "";
192    String stepHash = "";
193    String stepParameters = "";
194    String stepStatus = "";
195   
196    if (!hasDelimiters) {
197      stepName = "Unknown";
198      stepHash = "Unknown";
199      stepStatus = message;
200    } else {
201      // Extract the fields of the status message
202      stepHash = message.substring(0, message.indexOf('|'));
203      message = message.substring(message.indexOf('|') + 1,
204          message.length());
205      // See if there is a unique object ID in the stepHash string
206      if (stepHash.indexOf('$') > 0) {
207        // Extract the step name
208        stepName = stepHash.substring(0, stepHash.indexOf('$'));
209      } else {
210        stepName = stepHash;
211      }
212     
213      // See if there are any step parameters to extract
214      if (message.indexOf('|') > 0) {
215        stepParameters = message.substring(0, message.indexOf('|'));
216        stepStatus = message.substring(message.indexOf('|') + 1, 
217            message.length());
218      } else {
219        // set the status message to the remainder
220        stepStatus = message;
221      }
222    }
223   
224    // Now see if this step is in the hashmap
225    if (m_tableIndexes.containsKey(stepHash)) {
226      // Get the row number and update the table model...
227      final Integer rowNum = m_tableIndexes.get(stepHash);
228      if (stepStatus.equalsIgnoreCase("remove") ||
229          stepStatus.equalsIgnoreCase("remove.")) {
230       
231        //m_tableModel.fireTableDataChanged();
232        m_tableIndexes.remove(stepHash);
233        m_timers.get(stepHash).stop();
234        m_timers.remove(stepHash);
235       
236        // now need to decrement all the row indexes of
237        // any rows greater than this one
238        Iterator<String> i = m_tableIndexes.keySet().iterator();
239        while (i.hasNext()) {
240          String nextKey = i.next();
241          int index = m_tableIndexes.get(nextKey).intValue();
242          if (index > rowNum.intValue()) {
243            index--;
244            //System.err.println("*** " + nextKey + " needs decrementing to " + index);
245            m_tableIndexes.put(nextKey, index);
246//            System.err.println("new index " + m_rows.get(nextKey).intValue());
247          }
248        }
249       
250        // Remove the entry...
251        if (!SwingUtilities.isEventDispatchThread()) {
252          try {
253            SwingUtilities.invokeLater(new Runnable() {
254              public void run() {
255                m_tableModel.removeRow(rowNum);
256              }
257            });
258          } catch (Exception ex) {
259            ex.printStackTrace();
260          }
261        } else {
262          m_tableModel.removeRow(rowNum);
263        }
264      } else {
265        final String stepNameCopy = stepName;
266        final String stepStatusCopy = stepStatus;
267        final String stepParametersCopy = stepParameters;
268
269        if (!SwingUtilities.isEventDispatchThread()) {
270          try {
271            SwingUtilities.invokeLater(new Runnable() {
272              public void run() {
273                // ERROR overrides INTERRUPTED
274                if (!(stepStatusCopy.startsWith("INTERRUPTED") &&
275                    ((String)m_tableModel.getValueAt(rowNum.intValue(), 3)).startsWith("ERROR"))) {
276                  m_tableModel.setValueAt(stepNameCopy, rowNum.intValue(), 0);
277                  m_tableModel.setValueAt(stepParametersCopy, rowNum.intValue(), 1);
278                  m_tableModel.setValueAt(m_table.getValueAt(rowNum.intValue(), 2), rowNum.intValue(), 2);
279                  m_tableModel.setValueAt(stepStatusCopy, rowNum.intValue(), 3);
280                }
281              }
282            });
283          } catch (Exception ex) {
284            ex.printStackTrace();
285          }
286        } else {
287          if (!(stepStatusCopy.startsWith("INTERRUPTED") &&
288              ((String)m_tableModel.getValueAt(rowNum.intValue(), 3)).startsWith("ERROR"))) {
289            m_tableModel.setValueAt(stepNameCopy, rowNum.intValue(), 0);
290            m_tableModel.setValueAt(stepParametersCopy, rowNum.intValue(), 1);
291            m_tableModel.setValueAt(m_table.getValueAt(rowNum.intValue(), 2), rowNum.intValue(), 2);
292            m_tableModel.setValueAt(stepStatusCopy, rowNum.intValue(), 3);
293          }
294        }
295        if (stepStatus.startsWith("ERROR") ||
296            stepStatus.startsWith("INTERRUPTED") ||
297            stepStatus.equalsIgnoreCase("finished") ||
298            stepStatus.equalsIgnoreCase("finished.") ||
299            stepStatus.equalsIgnoreCase("done") ||
300            stepStatus.equalsIgnoreCase("done.")) {
301          // stop the timer.
302          m_timers.get(stepHash).stop();
303        } else if (!m_timers.get(stepHash).isRunning()) {
304          // need to create a new one in order to reset the
305          // elapsed time.
306          installTimer(stepHash);
307        }
308      //  m_tableModel.fireTableCellUpdated(rowNum.intValue(), 3);
309      }
310    } else if (!stepStatus.equalsIgnoreCase("Remove") &&
311        !stepStatus.equalsIgnoreCase("Remove.")) {
312      // Add this one to the hash map
313      int numKeys = m_tableIndexes.keySet().size();
314      m_tableIndexes.put(stepHash, numKeys);
315     
316      // Now add a row to the table model
317      final Object[] newRow = new Object[4];
318      newRow[0] = stepName;
319      newRow[1] = stepParameters;
320      newRow[2] = "-";
321      newRow[3] = stepStatus;
322      final String stepHashCopy = stepHash;
323      try {
324        if (!SwingUtilities.isEventDispatchThread()) {
325          SwingUtilities.invokeLater(new Runnable() {
326            public void run() {
327              m_tableModel.addRow(newRow);
328              //m_tableModel.fireTableDataChanged();
329            }
330          });
331        } else {
332          m_tableModel.addRow(newRow);
333        }
334       
335        installTimer(stepHashCopy);
336      } catch (Exception ex) {
337        ex.printStackTrace();
338      }
339    }
340  }
341 
342  private void installTimer(final String stepHash) {
343    final long startTime = System.currentTimeMillis();
344    Timer newTimer = new Timer(1000, new ActionListener() {
345      public void actionPerformed(ActionEvent e) {
346        synchronized(LogPanel.this) {
347          if (m_tableIndexes.containsKey(stepHash)) {
348            final Integer rn = m_tableIndexes.get(stepHash);
349            long elapsed = System.currentTimeMillis() - startTime;
350            long seconds = elapsed / 1000;
351            long minutes = seconds / 60;
352            final long hours = minutes / 60;
353            seconds = seconds - (minutes * 60);
354            minutes = minutes - (hours * 60);
355            final long seconds2 = seconds;
356            final long minutes2 = minutes;
357            if (!SwingUtilities.isEventDispatchThread()) {
358              try {
359              SwingUtilities.invokeLater(new Runnable() {
360                public void run() {
361                  m_tableModel.
362                    setValueAt("" + hours + ":" + minutes2 + ":" + seconds2, rn.intValue(), 2);
363                }
364              });
365              } catch (Exception ex) {
366                ex.printStackTrace();
367              }
368            } else {
369              m_tableModel.
370                setValueAt("" + hours + ":" + minutes2 + ":" + seconds2, rn.intValue(), 2);
371            }
372          }
373        }
374      }
375    });
376    m_timers.put(stepHash, newTimer);
377    newTimer.start();
378  }
379
380  /**
381   * Main method to test this class.
382   *
383   * @param args any arguments (unused)
384   */
385  public static void main(String[] args) {
386    try {
387      final javax.swing.JFrame jf = new javax.swing.JFrame("Status/Log Panel");
388     
389      jf.getContentPane().setLayout(new BorderLayout());
390      final LogPanel lp = new LogPanel();
391      jf.getContentPane().add(lp, BorderLayout.CENTER);
392     
393      jf.getContentPane().add(lp, BorderLayout.CENTER);
394      jf.addWindowListener(new java.awt.event.WindowAdapter() {
395        public void windowClosing(java.awt.event.WindowEvent e) {
396          jf.dispose();
397          System.exit(0);
398        }
399      });
400      jf.pack();
401      jf.setVisible(true);
402      lp.statusMessage("Step 1|Some options here|A status message");
403      lp.statusMessage("Step 2$hashkey|Status message: no options");
404      Thread.sleep(3000);
405      lp.statusMessage("Step 2$hashkey|Funky Chickens!!!");
406      Thread.sleep(3000);
407      lp.statusMessage("Step 1|Some options here|finished");
408      //lp.statusMessage("Step 1|Some options here|back again!");
409      Thread.sleep(3000);
410      lp.statusMessage("Step 2$hashkey|ERROR! More Funky Chickens!!!");
411      Thread.sleep(3000);
412      lp.statusMessage("Step 2$hashkey|WARNING - now a warning...");
413      Thread.sleep(3000);
414      lp.statusMessage("Step 2$hashkey|Back to normal.");
415      Thread.sleep(3000);
416      lp.statusMessage("Step 2$hashkey|INTERRUPTED.");
417     
418    } catch (Exception ex) {
419      ex.printStackTrace();
420    }
421  }
422}
Note: See TracBrowser for help on using the repository browser.