001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 * 
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 * 
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.log4j.lf5.viewer;
018
019import java.awt.BorderLayout;
020import java.awt.Color;
021import java.awt.Component;
022import java.awt.Dimension;
023import java.awt.FlowLayout;
024import java.awt.Font;
025import java.awt.GraphicsEnvironment;
026import java.awt.Toolkit;
027import java.awt.event.ActionEvent;
028import java.awt.event.ActionListener;
029import java.awt.event.WindowAdapter;
030import java.awt.event.WindowEvent;
031import java.io.File;
032import java.io.IOException;
033import java.io.InputStream;
034import java.net.MalformedURLException;
035import java.net.URL;
036import java.util.ArrayList;
037import java.util.HashMap;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Map;
041import java.util.StringTokenizer;
042import java.util.Vector;
043
044import javax.swing.BorderFactory;
045import javax.swing.ImageIcon;
046import javax.swing.JButton;
047import javax.swing.JCheckBoxMenuItem;
048import javax.swing.JColorChooser;
049import javax.swing.JComboBox;
050import javax.swing.JFileChooser;
051import javax.swing.JFrame;
052import javax.swing.JLabel;
053import javax.swing.JMenu;
054import javax.swing.JMenuBar;
055import javax.swing.JMenuItem;
056import javax.swing.JOptionPane;
057import javax.swing.JPanel;
058import javax.swing.JScrollPane;
059import javax.swing.JSplitPane;
060import javax.swing.JTextArea;
061import javax.swing.JToolBar;
062import javax.swing.KeyStroke;
063import javax.swing.SwingUtilities;
064
065import org.apache.log4j.lf5.LogLevel;
066import org.apache.log4j.lf5.LogRecord;
067import org.apache.log4j.lf5.LogRecordFilter;
068import org.apache.log4j.lf5.util.DateFormatManager;
069import org.apache.log4j.lf5.util.LogFileParser;
070import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
071import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
072import org.apache.log4j.lf5.viewer.configure.ConfigurationManager;
073import org.apache.log4j.lf5.viewer.configure.MRUFileManager;
074
075/**
076 * LogBrokerMonitor
077 *.
078 * @author Michael J. Sikorsky
079 * @author Robert Shaw
080 * @author Brad Marlborough
081 * @author Richard Wan
082 * @author Brent Sprecher
083 * @author Richard Hurst
084 */
085
086// Contributed by ThoughtWorks Inc.
087
088public class LogBrokerMonitor {
089  //--------------------------------------------------------------------------
090  //   Constants:
091  //--------------------------------------------------------------------------
092
093  public static final String DETAILED_VIEW = "Detailed";
094//    public static final String STANDARD_VIEW = "Standard";
095//    public static final String COMPACT_VIEW = "Compact";
096  //--------------------------------------------------------------------------
097  //   Protected Variables:
098  //--------------------------------------------------------------------------
099  protected JFrame _logMonitorFrame;
100  protected int _logMonitorFrameWidth = 550;
101  protected int _logMonitorFrameHeight = 500;
102  protected LogTable _table;
103  protected CategoryExplorerTree _categoryExplorerTree;
104  protected String _searchText;
105  protected String _NDCTextFilter = "";
106  protected LogLevel _leastSevereDisplayedLogLevel = LogLevel.DEBUG;
107
108  protected JScrollPane _logTableScrollPane;
109  protected JLabel _statusLabel;
110  protected Object _lock = new Object();
111  protected JComboBox _fontSizeCombo;
112
113  protected int _fontSize = 10;
114  protected String _fontName = "Dialog";
115  protected String _currentView = DETAILED_VIEW;
116
117  protected boolean _loadSystemFonts = false;
118  protected boolean _trackTableScrollPane = true;
119  protected Dimension _lastTableViewportSize;
120  protected boolean _callSystemExitOnClose = false;
121  protected List _displayedLogBrokerProperties = new Vector();
122
123  protected Map _logLevelMenuItems = new HashMap();
124  protected Map _logTableColumnMenuItems = new HashMap();
125
126  protected List _levels = null;
127  protected List _columns = null;
128  protected boolean _isDisposed = false;
129
130  protected ConfigurationManager _configurationManager = null;
131  protected MRUFileManager _mruFileManager = null;
132  protected File _fileLocation = null;
133
134  //--------------------------------------------------------------------------
135  //   Private Variables:
136  //--------------------------------------------------------------------------
137
138  //--------------------------------------------------------------------------
139  //   Constructors:
140  //--------------------------------------------------------------------------
141
142  /**
143   * Construct a LogBrokerMonitor.
144   */
145  public LogBrokerMonitor(List logLevels) {
146
147    _levels = logLevels;
148    _columns = LogTableColumn.getLogTableColumns();
149    // This allows us to use the LogBroker in command line tools and
150    // have the option for it to shutdown.
151
152    String callSystemExitOnClose =
153        System.getProperty("monitor.exit");
154    if (callSystemExitOnClose == null) {
155      callSystemExitOnClose = "false";
156    }
157    callSystemExitOnClose = callSystemExitOnClose.trim().toLowerCase();
158
159    if (callSystemExitOnClose.equals("true")) {
160      _callSystemExitOnClose = true;
161    }
162
163    initComponents();
164
165
166    _logMonitorFrame.addWindowListener(
167        new LogBrokerMonitorWindowAdaptor(this));
168
169  }
170
171  //--------------------------------------------------------------------------
172  //   Public Methods:
173  //--------------------------------------------------------------------------
174
175  /**
176   * Show the frame for the LogBrokerMonitor. Dispatched to the
177   * swing thread.
178   */
179  public void show(final int delay) {
180    if (_logMonitorFrame.isVisible()) {
181      return;
182    }
183    // This request is very low priority, let other threads execute first.
184    SwingUtilities.invokeLater(new Runnable() {
185      public void run() {
186        Thread.yield();
187        pause(delay);
188        _logMonitorFrame.setVisible(true);
189      }
190    });
191  }
192
193  public void show() {
194    show(0);
195  }
196
197  /**
198   * Dispose of the frame for the LogBrokerMonitor.
199   */
200  public void dispose() {
201    _logMonitorFrame.dispose();
202    _isDisposed = true;
203
204    if (_callSystemExitOnClose == true) {
205      System.exit(0);
206    }
207  }
208
209  /**
210   * Hide the frame for the LogBrokerMonitor.
211   */
212  public void hide() {
213    _logMonitorFrame.setVisible(false);
214  }
215
216  /**
217   * Get the DateFormatManager for formatting dates.
218   */
219  public DateFormatManager getDateFormatManager() {
220    return _table.getDateFormatManager();
221  }
222
223  /**
224   * Set the date format manager for formatting dates.
225   */
226  public void setDateFormatManager(DateFormatManager dfm) {
227    _table.setDateFormatManager(dfm);
228  }
229
230  /**
231   * Get the value of whether or not System.exit() will be called
232   * when the LogBrokerMonitor is closed.
233   */
234  public boolean getCallSystemExitOnClose() {
235    return _callSystemExitOnClose;
236  }
237
238  /**
239   * Set the value of whether or not System.exit() will be called
240   * when the LogBrokerMonitor is closed.
241   */
242  public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
243    _callSystemExitOnClose = callSystemExitOnClose;
244  }
245
246  /**
247   * Add a log record message to be displayed in the LogTable.
248   * This method is thread-safe as it posts requests to the SwingThread
249   * rather than processing directly.
250   */
251  public void addMessage(final LogRecord lr) {
252    if (_isDisposed == true) {
253      // If the frame has been disposed of, do not log any more
254      // messages.
255      return;
256    }
257
258    SwingUtilities.invokeLater(new Runnable() {
259      public void run() {
260        _categoryExplorerTree.getExplorerModel().addLogRecord(lr);
261        _table.getFilteredLogTableModel().addLogRecord(lr); // update table
262        updateStatusLabel(); // show updated counts
263      }
264    });
265  }
266
267  public void setMaxNumberOfLogRecords(int maxNumberOfLogRecords) {
268    _table.getFilteredLogTableModel().setMaxNumberOfLogRecords(maxNumberOfLogRecords);
269  }
270
271  public JFrame getBaseFrame() {
272    return _logMonitorFrame;
273  }
274
275  public void setTitle(String title) {
276    _logMonitorFrame.setTitle(title + " - LogFactor5");
277  }
278
279  public void setFrameSize(int width, int height) {
280    Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
281    if (0 < width && width < screen.width) {
282      _logMonitorFrameWidth = width;
283    }
284    if (0 < height && height < screen.height) {
285      _logMonitorFrameHeight = height;
286    }
287    updateFrameSize();
288  }
289
290  public void setFontSize(int fontSize) {
291    changeFontSizeCombo(_fontSizeCombo, fontSize);
292    // setFontSizeSilently(actualFontSize); - changeFontSizeCombo fires event
293    // refreshDetailTextArea();
294  }
295
296  public void addDisplayedProperty(Object messageLine) {
297    _displayedLogBrokerProperties.add(messageLine);
298  }
299
300  public Map getLogLevelMenuItems() {
301    return _logLevelMenuItems;
302  }
303
304  public Map getLogTableColumnMenuItems() {
305    return _logTableColumnMenuItems;
306  }
307
308  public JCheckBoxMenuItem getTableColumnMenuItem(LogTableColumn column) {
309    return getLogTableColumnMenuItem(column);
310  }
311
312  public CategoryExplorerTree getCategoryExplorerTree() {
313    return _categoryExplorerTree;
314  }
315
316  // Added in version 1.2 - gets the value of the NDC text filter
317  // This value is set back to null each time the Monitor is initialized.
318  public String getNDCTextFilter() {
319    return _NDCTextFilter;
320  }
321
322  // Added in version 1.2 - sets the NDC Filter based on
323  // a String passed in by the user.  This value is persisted
324  // in the XML Configuration file.
325  public void setNDCLogRecordFilter(String textFilter) {
326    _table.getFilteredLogTableModel().
327        setLogRecordFilter(createNDCLogRecordFilter(textFilter));
328  }
329  //--------------------------------------------------------------------------
330  //   Protected Methods:
331  //--------------------------------------------------------------------------
332
333  protected void setSearchText(String text) {
334    _searchText = text;
335  }
336
337  // Added in version 1.2 - Sets the text filter for the NDC
338  protected void setNDCTextFilter(String text) {
339    // if no value is set, set it to a blank string
340    // otherwise use the value provided
341    if (text == null) {
342      _NDCTextFilter = "";
343    } else {
344      _NDCTextFilter = text;
345    }
346  }
347
348  // Added in version 1.2 - Uses a different filter that sorts
349  // based on an NDC string passed in by the user.  If the string
350  // is null or is an empty string, we do nothing.
351  protected void sortByNDC() {
352    String text = _NDCTextFilter;
353    if (text == null || text.length() == 0) {
354      return;
355    }
356
357    // Use new NDC filter
358    _table.getFilteredLogTableModel().
359        setLogRecordFilter(createNDCLogRecordFilter(text));
360  }
361
362  protected void findSearchText() {
363    String text = _searchText;
364    if (text == null || text.length() == 0) {
365      return;
366    }
367    int startRow = getFirstSelectedRow();
368    int foundRow = findRecord(
369        startRow,
370        text,
371        _table.getFilteredLogTableModel().getFilteredRecords()
372    );
373    selectRow(foundRow);
374  }
375
376  protected int getFirstSelectedRow() {
377    return _table.getSelectionModel().getMinSelectionIndex();
378  }
379
380  protected void selectRow(int foundRow) {
381    if (foundRow == -1) {
382      String message = _searchText + " not found.";
383      JOptionPane.showMessageDialog(
384          _logMonitorFrame,
385          message,
386          "Text not found",
387          JOptionPane.INFORMATION_MESSAGE
388      );
389      return;
390    }
391    LF5SwingUtils.selectRow(foundRow, _table, _logTableScrollPane);
392  }
393
394  protected int findRecord(
395      int startRow,
396      String searchText,
397      List records
398      ) {
399    if (startRow < 0) {
400      startRow = 0; // start at first element if no rows are selected
401    } else {
402      startRow++; // start after the first selected row
403    }
404    int len = records.size();
405
406    for (int i = startRow; i < len; i++) {
407      if (matches((LogRecord) records.get(i), searchText)) {
408        return i; // found a record
409      }
410    }
411    // wrap around to beginning if when we reach the end with no match
412    len = startRow;
413    for (int i = 0; i < len; i++) {
414      if (matches((LogRecord) records.get(i), searchText)) {
415        return i; // found a record
416      }
417    }
418    // nothing found
419    return -1;
420  }
421
422  /**
423   * Check to see if the any records contain the search string.
424   * Searching now supports NDC messages and date.
425   */
426  protected boolean matches(LogRecord record, String text) {
427    String message = record.getMessage();
428    String NDC = record.getNDC();
429
430    if (message == null && NDC == null || text == null) {
431      return false;
432    }
433    if (message.toLowerCase().indexOf(text.toLowerCase()) == -1 &&
434        NDC.toLowerCase().indexOf(text.toLowerCase()) == -1) {
435      return false;
436    }
437
438    return true;
439  }
440
441  /**
442   * When the fontsize of a JTextArea is changed, the word-wrapped lines
443   * may become garbled.  This method clears and resets the text of the
444   * text area.
445   */
446  protected void refresh(JTextArea textArea) {
447    String text = textArea.getText();
448    textArea.setText("");
449    textArea.setText(text);
450  }
451
452  protected void refreshDetailTextArea() {
453    refresh(_table._detailTextArea);
454  }
455
456  protected void clearDetailTextArea() {
457    _table._detailTextArea.setText("");
458  }
459
460  /**
461   * Changes the font selection in the combo box and returns the
462   * size actually selected.
463   * @return -1 if unable to select an appropriate font
464   */
465  protected int changeFontSizeCombo(JComboBox box, int requestedSize) {
466    int len = box.getItemCount();
467    int currentValue;
468    Object currentObject;
469    Object selectedObject = box.getItemAt(0);
470    int selectedValue = Integer.parseInt(String.valueOf(selectedObject));
471    for (int i = 0; i < len; i++) {
472      currentObject = box.getItemAt(i);
473      currentValue = Integer.parseInt(String.valueOf(currentObject));
474      if (selectedValue < currentValue && currentValue <= requestedSize) {
475        selectedValue = currentValue;
476        selectedObject = currentObject;
477      }
478    }
479    box.setSelectedItem(selectedObject);
480    return selectedValue;
481  }
482
483  /**
484   * Does not update gui or cause any events to be fired.
485   */
486  protected void setFontSizeSilently(int fontSize) {
487    _fontSize = fontSize;
488    setFontSize(_table._detailTextArea, fontSize);
489    selectRow(0);
490    setFontSize(_table, fontSize);
491  }
492
493  protected void setFontSize(Component component, int fontSize) {
494    Font oldFont = component.getFont();
495    Font newFont =
496        new Font(oldFont.getFontName(), oldFont.getStyle(), fontSize);
497    component.setFont(newFont);
498  }
499
500  protected void updateFrameSize() {
501    _logMonitorFrame.setSize(_logMonitorFrameWidth, _logMonitorFrameHeight);
502    centerFrame(_logMonitorFrame);
503  }
504
505  protected void pause(int millis) {
506    try {
507      Thread.sleep(millis);
508    } catch (InterruptedException e) {
509
510    }
511  }
512
513  protected void initComponents() {
514    //
515    // Configure the Frame.
516    //
517    _logMonitorFrame = new JFrame("LogFactor5");
518
519    _logMonitorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
520
521    String resource =
522        "/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif";
523    URL lf5IconURL = getClass().getResource(resource);
524
525    if (lf5IconURL != null) {
526      _logMonitorFrame.setIconImage(new ImageIcon(lf5IconURL).getImage());
527    }
528    updateFrameSize();
529
530    //
531    // Configure the LogTable.
532    //
533    JTextArea detailTA = createDetailTextArea();
534    JScrollPane detailTAScrollPane = new JScrollPane(detailTA);
535    _table = new LogTable(detailTA);
536    setView(_currentView, _table);
537    _table.setFont(new Font(_fontName, Font.PLAIN, _fontSize));
538    _logTableScrollPane = new JScrollPane(_table);
539
540    if (_trackTableScrollPane) {
541      _logTableScrollPane.getVerticalScrollBar().addAdjustmentListener(
542          new TrackingAdjustmentListener()
543      );
544    }
545
546
547    // Configure the SplitPane between the LogTable & DetailTextArea
548    //
549
550    JSplitPane tableViewerSplitPane = new JSplitPane();
551    tableViewerSplitPane.setOneTouchExpandable(true);
552    tableViewerSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
553    tableViewerSplitPane.setLeftComponent(_logTableScrollPane);
554    tableViewerSplitPane.setRightComponent(detailTAScrollPane);
555    // Make sure to do this last..
556    //tableViewerSplitPane.setDividerLocation(1.0); Doesn't work
557    //the same under 1.2.x & 1.3
558    // "350" is a magic number that provides the correct default
559    // behaviour under 1.2.x & 1.3.  For example, bumping this
560    // number to 400, causes the pane to be completely open in 1.2.x
561    // and closed in 1.3
562    tableViewerSplitPane.setDividerLocation(350);
563
564    //
565    // Configure the CategoryExplorer
566    //
567
568    _categoryExplorerTree = new CategoryExplorerTree();
569
570    _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
571
572    JScrollPane categoryExplorerTreeScrollPane =
573        new JScrollPane(_categoryExplorerTree);
574    categoryExplorerTreeScrollPane.setPreferredSize(new Dimension(130, 400));
575
576    // Load most recently used file list
577    _mruFileManager = new MRUFileManager();
578
579    //
580    // Configure the SplitPane between the CategoryExplorer & (LogTable/Detail)
581    //
582
583    JSplitPane splitPane = new JSplitPane();
584    splitPane.setOneTouchExpandable(true);
585    splitPane.setRightComponent(tableViewerSplitPane);
586    splitPane.setLeftComponent(categoryExplorerTreeScrollPane);
587    // Do this last.
588    splitPane.setDividerLocation(130);
589    //
590    // Add the MenuBar, StatusArea, CategoryExplorer|LogTable to the
591    // LogMonitorFrame.
592    //
593    _logMonitorFrame.getRootPane().setJMenuBar(createMenuBar());
594    _logMonitorFrame.getContentPane().add(splitPane, BorderLayout.CENTER);
595    _logMonitorFrame.getContentPane().add(createToolBar(),
596        BorderLayout.NORTH);
597    _logMonitorFrame.getContentPane().add(createStatusArea(),
598        BorderLayout.SOUTH);
599
600    makeLogTableListenToCategoryExplorer();
601    addTableModelProperties();
602
603    //
604    // Configure ConfigurationManager
605    //
606    _configurationManager = new ConfigurationManager(this, _table);
607
608  }
609
610  protected LogRecordFilter createLogRecordFilter() {
611    LogRecordFilter result = new LogRecordFilter() {
612      public boolean passes(LogRecord record) {
613        CategoryPath path = new CategoryPath(record.getCategory());
614        return
615            getMenuItem(record.getLevel()).isSelected() &&
616            _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
617      }
618    };
619    return result;
620  }
621
622  // Added in version 1.2 - Creates a new filter that sorts records based on
623  // an NDC string passed in by the user.
624  protected LogRecordFilter createNDCLogRecordFilter(String text) {
625    _NDCTextFilter = text;
626    LogRecordFilter result = new LogRecordFilter() {
627      public boolean passes(LogRecord record) {
628        String NDC = record.getNDC();
629        CategoryPath path = new CategoryPath(record.getCategory());
630        if (NDC == null || _NDCTextFilter == null) {
631          return false;
632        } else if (NDC.toLowerCase().indexOf(_NDCTextFilter.toLowerCase()) == -1) {
633          return false;
634        } else {
635          return getMenuItem(record.getLevel()).isSelected() &&
636              _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
637        }
638      }
639    };
640
641    return result;
642  }
643
644
645  protected void updateStatusLabel() {
646    _statusLabel.setText(getRecordsDisplayedMessage());
647  }
648
649  protected String getRecordsDisplayedMessage() {
650    FilteredLogTableModel model = _table.getFilteredLogTableModel();
651    return getStatusText(model.getRowCount(), model.getTotalRowCount());
652  }
653
654  protected void addTableModelProperties() {
655    final FilteredLogTableModel model = _table.getFilteredLogTableModel();
656
657    addDisplayedProperty(new Object() {
658      public String toString() {
659        return getRecordsDisplayedMessage();
660      }
661    });
662    addDisplayedProperty(new Object() {
663      public String toString() {
664        return "Maximum number of displayed LogRecords: "
665            + model._maxNumberOfLogRecords;
666      }
667    });
668  }
669
670  protected String getStatusText(int displayedRows, int totalRows) {
671    StringBuffer result = new StringBuffer();
672    result.append("Displaying: ");
673    result.append(displayedRows);
674    result.append(" records out of a total of: ");
675    result.append(totalRows);
676    result.append(" records.");
677    return result.toString();
678  }
679
680  protected void makeLogTableListenToCategoryExplorer() {
681    ActionListener listener = new ActionListener() {
682      public void actionPerformed(ActionEvent e) {
683        _table.getFilteredLogTableModel().refresh();
684        updateStatusLabel();
685      }
686    };
687    _categoryExplorerTree.getExplorerModel().addActionListener(listener);
688  }
689
690  protected JPanel createStatusArea() {
691    JPanel statusArea = new JPanel();
692    JLabel status =
693        new JLabel("No log records to display.");
694    _statusLabel = status;
695    status.setHorizontalAlignment(JLabel.LEFT);
696
697    statusArea.setBorder(BorderFactory.createEtchedBorder());
698    statusArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
699    statusArea.add(status);
700
701    return (statusArea);
702  }
703
704  protected JTextArea createDetailTextArea() {
705    JTextArea detailTA = new JTextArea();
706    detailTA.setFont(new Font("Monospaced", Font.PLAIN, 14));
707    detailTA.setTabSize(3);
708    detailTA.setLineWrap(true);
709    detailTA.setWrapStyleWord(false);
710    return (detailTA);
711  }
712
713  protected JMenuBar createMenuBar() {
714    JMenuBar menuBar = new JMenuBar();
715    menuBar.add(createFileMenu());
716    menuBar.add(createEditMenu());
717    menuBar.add(createLogLevelMenu());
718    menuBar.add(createViewMenu());
719    menuBar.add(createConfigureMenu());
720    menuBar.add(createHelpMenu());
721
722    return (menuBar);
723  }
724
725  protected JMenu createLogLevelMenu() {
726    JMenu result = new JMenu("Log Level");
727    result.setMnemonic('l');
728    Iterator levels = getLogLevels();
729    while (levels.hasNext()) {
730      result.add(getMenuItem((LogLevel) levels.next()));
731    }
732
733    result.addSeparator();
734    result.add(createAllLogLevelsMenuItem());
735    result.add(createNoLogLevelsMenuItem());
736    result.addSeparator();
737    result.add(createLogLevelColorMenu());
738    result.add(createResetLogLevelColorMenuItem());
739
740    return result;
741  }
742
743  protected JMenuItem createAllLogLevelsMenuItem() {
744    JMenuItem result = new JMenuItem("Show all LogLevels");
745    result.setMnemonic('s');
746    result.addActionListener(new ActionListener() {
747      public void actionPerformed(ActionEvent e) {
748        selectAllLogLevels(true);
749        _table.getFilteredLogTableModel().refresh();
750        updateStatusLabel();
751      }
752    });
753    return result;
754  }
755
756  protected JMenuItem createNoLogLevelsMenuItem() {
757    JMenuItem result = new JMenuItem("Hide all LogLevels");
758    result.setMnemonic('h');
759    result.addActionListener(new ActionListener() {
760      public void actionPerformed(ActionEvent e) {
761        selectAllLogLevels(false);
762        _table.getFilteredLogTableModel().refresh();
763        updateStatusLabel();
764      }
765    });
766    return result;
767  }
768
769  protected JMenu createLogLevelColorMenu() {
770    JMenu colorMenu = new JMenu("Configure LogLevel Colors");
771    colorMenu.setMnemonic('c');
772    Iterator levels = getLogLevels();
773    while (levels.hasNext()) {
774      colorMenu.add(createSubMenuItem((LogLevel) levels.next()));
775    }
776
777    return colorMenu;
778  }
779
780  protected JMenuItem createResetLogLevelColorMenuItem() {
781    JMenuItem result = new JMenuItem("Reset LogLevel Colors");
782    result.setMnemonic('r');
783    result.addActionListener(new ActionListener() {
784      public void actionPerformed(ActionEvent e) {
785        // reset the level colors in the map
786        LogLevel.resetLogLevelColorMap();
787
788        // refresh the table
789        _table.getFilteredLogTableModel().refresh();
790      }
791    });
792    return result;
793  }
794
795  protected void selectAllLogLevels(boolean selected) {
796    Iterator levels = getLogLevels();
797    while (levels.hasNext()) {
798      getMenuItem((LogLevel) levels.next()).setSelected(selected);
799    }
800  }
801
802  protected JCheckBoxMenuItem getMenuItem(LogLevel level) {
803    JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logLevelMenuItems.get(level));
804    if (result == null) {
805      result = createMenuItem(level);
806      _logLevelMenuItems.put(level, result);
807    }
808    return result;
809  }
810
811  protected JMenuItem createSubMenuItem(LogLevel level) {
812    final JMenuItem result = new JMenuItem(level.toString());
813    final LogLevel logLevel = level;
814    result.setMnemonic(level.toString().charAt(0));
815    result.addActionListener(new ActionListener() {
816      public void actionPerformed(ActionEvent e) {
817        showLogLevelColorChangeDialog(result, logLevel);
818      }
819    });
820
821    return result;
822
823  }
824
825  protected void showLogLevelColorChangeDialog(JMenuItem result, LogLevel level) {
826    JMenuItem menuItem = result;
827    Color newColor = JColorChooser.showDialog(
828        _logMonitorFrame,
829        "Choose LogLevel Color",
830        result.getForeground());
831
832    if (newColor != null) {
833      // set the color for the record
834      level.setLogLevelColorMap(level, newColor);
835      _table.getFilteredLogTableModel().refresh();
836    }
837
838  }
839
840  protected JCheckBoxMenuItem createMenuItem(LogLevel level) {
841    JCheckBoxMenuItem result = new JCheckBoxMenuItem(level.toString());
842    result.setSelected(true);
843    result.setMnemonic(level.toString().charAt(0));
844    result.addActionListener(new ActionListener() {
845      public void actionPerformed(ActionEvent e) {
846        _table.getFilteredLogTableModel().refresh();
847        updateStatusLabel();
848      }
849    });
850    return result;
851  }
852
853  // view menu
854  protected JMenu createViewMenu() {
855    JMenu result = new JMenu("View");
856    result.setMnemonic('v');
857    Iterator columns = getLogTableColumns();
858    while (columns.hasNext()) {
859      result.add(getLogTableColumnMenuItem((LogTableColumn) columns.next()));
860    }
861
862    result.addSeparator();
863    result.add(createAllLogTableColumnsMenuItem());
864    result.add(createNoLogTableColumnsMenuItem());
865    return result;
866  }
867
868  protected JCheckBoxMenuItem getLogTableColumnMenuItem(LogTableColumn column) {
869    JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logTableColumnMenuItems.get(column));
870    if (result == null) {
871      result = createLogTableColumnMenuItem(column);
872      _logTableColumnMenuItems.put(column, result);
873    }
874    return result;
875  }
876
877  protected JCheckBoxMenuItem createLogTableColumnMenuItem(LogTableColumn column) {
878    JCheckBoxMenuItem result = new JCheckBoxMenuItem(column.toString());
879
880    result.setSelected(true);
881    result.setMnemonic(column.toString().charAt(0));
882    result.addActionListener(new ActionListener() {
883      public void actionPerformed(ActionEvent e) {
884        // update list of columns and reset the view
885        List selectedColumns = updateView();
886        _table.setView(selectedColumns);
887      }
888    });
889    return result;
890  }
891
892  protected List updateView() {
893    ArrayList updatedList = new ArrayList();
894    Iterator columnIterator = _columns.iterator();
895    while (columnIterator.hasNext()) {
896      LogTableColumn column = (LogTableColumn) columnIterator.next();
897      JCheckBoxMenuItem result = getLogTableColumnMenuItem(column);
898      // check and see if the checkbox is checked
899      if (result.isSelected()) {
900        updatedList.add(column);
901      }
902    }
903
904    return updatedList;
905  }
906
907  protected JMenuItem createAllLogTableColumnsMenuItem() {
908    JMenuItem result = new JMenuItem("Show all Columns");
909    result.setMnemonic('s');
910    result.addActionListener(new ActionListener() {
911      public void actionPerformed(ActionEvent e) {
912        selectAllLogTableColumns(true);
913        // update list of columns and reset the view
914        List selectedColumns = updateView();
915        _table.setView(selectedColumns);
916      }
917    });
918    return result;
919  }
920
921  protected JMenuItem createNoLogTableColumnsMenuItem() {
922    JMenuItem result = new JMenuItem("Hide all Columns");
923    result.setMnemonic('h');
924    result.addActionListener(new ActionListener() {
925      public void actionPerformed(ActionEvent e) {
926        selectAllLogTableColumns(false);
927        // update list of columns and reset the view
928        List selectedColumns = updateView();
929        _table.setView(selectedColumns);
930      }
931    });
932    return result;
933  }
934
935  protected void selectAllLogTableColumns(boolean selected) {
936    Iterator columns = getLogTableColumns();
937    while (columns.hasNext()) {
938      getLogTableColumnMenuItem((LogTableColumn) columns.next()).setSelected(selected);
939    }
940  }
941
942  protected JMenu createFileMenu() {
943    JMenu fileMenu = new JMenu("File");
944    fileMenu.setMnemonic('f');
945    JMenuItem exitMI;
946    fileMenu.add(createOpenMI());
947    fileMenu.add(createOpenURLMI());
948    fileMenu.addSeparator();
949    fileMenu.add(createCloseMI());
950    createMRUFileListMI(fileMenu);
951    fileMenu.addSeparator();
952    fileMenu.add(createExitMI());
953    return fileMenu;
954  }
955
956  /**
957   * Menu item added to allow log files to be opened with
958   * the LF5 GUI.
959   */
960  protected JMenuItem createOpenMI() {
961    JMenuItem result = new JMenuItem("Open...");
962    result.setMnemonic('o');
963    result.addActionListener(new ActionListener() {
964      public void actionPerformed(ActionEvent e) {
965        requestOpen();
966      }
967    });
968    return result;
969  }
970
971  /**
972   * Menu item added to allow log files loaded from a URL
973   * to be opened by the LF5 GUI.
974   */
975  protected JMenuItem createOpenURLMI() {
976    JMenuItem result = new JMenuItem("Open URL...");
977    result.setMnemonic('u');
978    result.addActionListener(new ActionListener() {
979      public void actionPerformed(ActionEvent e) {
980        requestOpenURL();
981      }
982    });
983    return result;
984  }
985
986  protected JMenuItem createCloseMI() {
987    JMenuItem result = new JMenuItem("Close");
988    result.setMnemonic('c');
989    result.setAccelerator(KeyStroke.getKeyStroke("control Q"));
990    result.addActionListener(new ActionListener() {
991      public void actionPerformed(ActionEvent e) {
992        requestClose();
993      }
994    });
995    return result;
996  }
997
998  /**
999   * Creates a Most Recently Used file list to be
1000   * displayed in the File menu
1001   */
1002  protected void createMRUFileListMI(JMenu menu) {
1003
1004    String[] files = _mruFileManager.getMRUFileList();
1005
1006    if (files != null) {
1007      menu.addSeparator();
1008      for (int i = 0; i < files.length; i++) {
1009        JMenuItem result = new JMenuItem((i + 1) + " " + files[i]);
1010        result.setMnemonic(i + 1);
1011        result.addActionListener(new ActionListener() {
1012          public void actionPerformed(ActionEvent e) {
1013            requestOpenMRU(e);
1014          }
1015        });
1016        menu.add(result);
1017      }
1018    }
1019  }
1020
1021  protected JMenuItem createExitMI() {
1022    JMenuItem result = new JMenuItem("Exit");
1023    result.setMnemonic('x');
1024    result.addActionListener(new ActionListener() {
1025      public void actionPerformed(ActionEvent e) {
1026        requestExit();
1027      }
1028    });
1029    return result;
1030  }
1031
1032  protected JMenu createConfigureMenu() {
1033    JMenu configureMenu = new JMenu("Configure");
1034    configureMenu.setMnemonic('c');
1035    configureMenu.add(createConfigureSave());
1036    configureMenu.add(createConfigureReset());
1037    configureMenu.add(createConfigureMaxRecords());
1038
1039    return configureMenu;
1040  }
1041
1042  protected JMenuItem createConfigureSave() {
1043    JMenuItem result = new JMenuItem("Save");
1044    result.setMnemonic('s');
1045    result.addActionListener(new ActionListener() {
1046      public void actionPerformed(ActionEvent e) {
1047        saveConfiguration();
1048      }
1049    });
1050
1051    return result;
1052  }
1053
1054  protected JMenuItem createConfigureReset() {
1055    JMenuItem result = new JMenuItem("Reset");
1056    result.setMnemonic('r');
1057    result.addActionListener(new ActionListener() {
1058      public void actionPerformed(ActionEvent e) {
1059        resetConfiguration();
1060      }
1061    });
1062
1063    return result;
1064  }
1065
1066  protected JMenuItem createConfigureMaxRecords() {
1067    JMenuItem result = new JMenuItem("Set Max Number of Records");
1068    result.setMnemonic('m');
1069    result.addActionListener(new ActionListener() {
1070      public void actionPerformed(ActionEvent e) {
1071        setMaxRecordConfiguration();
1072      }
1073    });
1074
1075    return result;
1076  }
1077
1078
1079  protected void saveConfiguration() {
1080    _configurationManager.save();
1081  }
1082
1083  protected void resetConfiguration() {
1084    _configurationManager.reset();
1085  }
1086
1087  protected void setMaxRecordConfiguration() {
1088    LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
1089        getBaseFrame(), "Set Max Number of Records", "", 10);
1090
1091    String temp = inputDialog.getText();
1092
1093    if (temp != null) {
1094      try {
1095        setMaxNumberOfLogRecords(Integer.parseInt(temp));
1096      } catch (NumberFormatException e) {
1097        LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1098            getBaseFrame(),
1099            "'" + temp + "' is an invalid parameter.\nPlease try again.");
1100        setMaxRecordConfiguration();
1101      }
1102    }
1103  }
1104
1105
1106  protected JMenu createHelpMenu() {
1107    JMenu helpMenu = new JMenu("Help");
1108    helpMenu.setMnemonic('h');
1109    helpMenu.add(createHelpProperties());
1110    return helpMenu;
1111  }
1112
1113  protected JMenuItem createHelpProperties() {
1114    final String title = "LogFactor5 Properties";
1115    final JMenuItem result = new JMenuItem(title);
1116    result.setMnemonic('l');
1117    result.addActionListener(new ActionListener() {
1118      public void actionPerformed(ActionEvent e) {
1119        showPropertiesDialog(title);
1120      }
1121    });
1122    return result;
1123  }
1124
1125  protected void showPropertiesDialog(String title) {
1126    JOptionPane.showMessageDialog(
1127        _logMonitorFrame,
1128        _displayedLogBrokerProperties.toArray(),
1129        title,
1130        JOptionPane.PLAIN_MESSAGE
1131    );
1132  }
1133
1134  protected JMenu createEditMenu() {
1135    JMenu editMenu = new JMenu("Edit");
1136    editMenu.setMnemonic('e');
1137    editMenu.add(createEditFindMI());
1138    editMenu.add(createEditFindNextMI());
1139    editMenu.addSeparator();
1140    editMenu.add(createEditSortNDCMI());
1141    editMenu.add(createEditRestoreAllNDCMI());
1142    return editMenu;
1143  }
1144
1145  protected JMenuItem createEditFindNextMI() {
1146    JMenuItem editFindNextMI = new JMenuItem("Find Next");
1147    editFindNextMI.setMnemonic('n');
1148    editFindNextMI.setAccelerator(KeyStroke.getKeyStroke("F3"));
1149    editFindNextMI.addActionListener(new ActionListener() {
1150      public void actionPerformed(ActionEvent e) {
1151        findSearchText();
1152      }
1153    });
1154    return editFindNextMI;
1155  }
1156
1157  protected JMenuItem createEditFindMI() {
1158    JMenuItem editFindMI = new JMenuItem("Find");
1159    editFindMI.setMnemonic('f');
1160    editFindMI.setAccelerator(KeyStroke.getKeyStroke("control F"));
1161
1162    editFindMI.addActionListener(
1163        new ActionListener() {
1164          public void actionPerformed(ActionEvent e) {
1165            String inputValue =
1166                JOptionPane.showInputDialog(
1167                    _logMonitorFrame,
1168                    "Find text: ",
1169                    "Search Record Messages",
1170                    JOptionPane.QUESTION_MESSAGE
1171                );
1172            setSearchText(inputValue);
1173            findSearchText();
1174          }
1175        }
1176
1177    );
1178    return editFindMI;
1179  }
1180
1181  // Added version 1.2 - Allows users to Sort Log Records by an
1182  // NDC text filter. A new LogRecordFilter was created to
1183  // sort the records.
1184  protected JMenuItem createEditSortNDCMI() {
1185    JMenuItem editSortNDCMI = new JMenuItem("Sort by NDC");
1186    editSortNDCMI.setMnemonic('s');
1187    editSortNDCMI.addActionListener(
1188        new ActionListener() {
1189          public void actionPerformed(ActionEvent e) {
1190            String inputValue =
1191                JOptionPane.showInputDialog(
1192                    _logMonitorFrame,
1193                    "Sort by this NDC: ",
1194                    "Sort Log Records by NDC",
1195                    JOptionPane.QUESTION_MESSAGE
1196                );
1197            setNDCTextFilter(inputValue);
1198            sortByNDC();
1199            _table.getFilteredLogTableModel().refresh();
1200            updateStatusLabel();
1201          }
1202        }
1203
1204    );
1205    return editSortNDCMI;
1206  }
1207
1208  // Added in version 1.2 - Resets the LogRecordFilter back to default
1209  // filter.
1210  protected JMenuItem createEditRestoreAllNDCMI() {
1211    JMenuItem editRestoreAllNDCMI = new JMenuItem("Restore all NDCs");
1212    editRestoreAllNDCMI.setMnemonic('r');
1213    editRestoreAllNDCMI.addActionListener(
1214        new ActionListener() {
1215          public void actionPerformed(ActionEvent e) {
1216            _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
1217            // reset the text filter
1218            setNDCTextFilter("");
1219            _table.getFilteredLogTableModel().refresh();
1220            updateStatusLabel();
1221          }
1222        }
1223    );
1224    return editRestoreAllNDCMI;
1225  }
1226
1227  protected JToolBar createToolBar() {
1228    JToolBar tb = new JToolBar();
1229    tb.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
1230    JComboBox fontCombo = new JComboBox();
1231    JComboBox fontSizeCombo = new JComboBox();
1232    _fontSizeCombo = fontSizeCombo;
1233
1234    ClassLoader cl = this.getClass().getClassLoader();
1235    if(cl == null) {
1236        cl = ClassLoader.getSystemClassLoader();
1237    }
1238    URL newIconURL = cl.getResource("org/apache/log4j/lf5/viewer/" +
1239        "images/channelexplorer_new.gif");
1240
1241    ImageIcon newIcon = null;
1242
1243    if (newIconURL != null) {
1244      newIcon = new ImageIcon(newIconURL);
1245    }
1246
1247    JButton newButton = new JButton("Clear Log Table");
1248
1249    if (newIcon != null) {
1250      newButton.setIcon(newIcon);
1251    }
1252
1253    newButton.setToolTipText("Clear Log Table.");
1254    //newButton.setBorder(BorderFactory.createEtchedBorder());
1255
1256    newButton.addActionListener(
1257        new ActionListener() {
1258          public void actionPerformed(ActionEvent e) {
1259            _table.clearLogRecords();
1260            _categoryExplorerTree.getExplorerModel().resetAllNodeCounts();
1261            updateStatusLabel();
1262            clearDetailTextArea();
1263            LogRecord.resetSequenceNumber();
1264          }
1265        }
1266    );
1267
1268    Toolkit tk = Toolkit.getDefaultToolkit();
1269    // This will actually grab all the fonts
1270
1271    String[] fonts;
1272
1273    if (_loadSystemFonts) {
1274      fonts = GraphicsEnvironment.
1275          getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
1276    } else {
1277      fonts = tk.getFontList();
1278    }
1279
1280    for (int j = 0; j < fonts.length; j++) {
1281      fontCombo.addItem(fonts[j]);
1282    }
1283
1284    fontCombo.setSelectedItem(_fontName);
1285
1286    fontCombo.addActionListener(
1287
1288        new ActionListener() {
1289          public void actionPerformed(ActionEvent e) {
1290            JComboBox box = (JComboBox) e.getSource();
1291            String font = (String) box.getSelectedItem();
1292            _table.setFont(new Font(font, Font.PLAIN, _fontSize));
1293            _fontName = font;
1294          }
1295        }
1296    );
1297
1298    fontSizeCombo.addItem("8");
1299    fontSizeCombo.addItem("9");
1300    fontSizeCombo.addItem("10");
1301    fontSizeCombo.addItem("12");
1302    fontSizeCombo.addItem("14");
1303    fontSizeCombo.addItem("16");
1304    fontSizeCombo.addItem("18");
1305    fontSizeCombo.addItem("24");
1306
1307    fontSizeCombo.setSelectedItem(String.valueOf(_fontSize));
1308    fontSizeCombo.addActionListener(
1309        new ActionListener() {
1310          public void actionPerformed(ActionEvent e) {
1311            JComboBox box = (JComboBox) e.getSource();
1312            String size = (String) box.getSelectedItem();
1313            int s = Integer.valueOf(size).intValue();
1314
1315            setFontSizeSilently(s);
1316            refreshDetailTextArea();
1317            _fontSize = s;
1318          }
1319        }
1320    );
1321
1322    tb.add(new JLabel(" Font: "));
1323    tb.add(fontCombo);
1324    tb.add(fontSizeCombo);
1325    tb.addSeparator();
1326    tb.addSeparator();
1327    tb.add(newButton);
1328
1329    newButton.setAlignmentY(0.5f);
1330    newButton.setAlignmentX(0.5f);
1331
1332    fontCombo.setMaximumSize(fontCombo.getPreferredSize());
1333    fontSizeCombo.setMaximumSize(
1334        fontSizeCombo.getPreferredSize());
1335
1336    return (tb);
1337  }
1338
1339//    protected void setView(String viewString, LogTable table) {
1340//        if (STANDARD_VIEW.equals(viewString)) {
1341//            table.setStandardView();
1342//        } else if (COMPACT_VIEW.equals(viewString)) {
1343//            table.setCompactView();
1344//        } else if (DETAILED_VIEW.equals(viewString)) {
1345//            table.setDetailedView();
1346//        } else {
1347//            String message = viewString + "does not match a supported view.";
1348//            throw new IllegalArgumentException(message);
1349//        }
1350//        _currentView = viewString;
1351//    }
1352
1353  protected void setView(String viewString, LogTable table) {
1354    if (DETAILED_VIEW.equals(viewString)) {
1355      table.setDetailedView();
1356    } else {
1357      String message = viewString + "does not match a supported view.";
1358      throw new IllegalArgumentException(message);
1359    }
1360    _currentView = viewString;
1361  }
1362
1363  protected JComboBox createLogLevelCombo() {
1364    JComboBox result = new JComboBox();
1365    Iterator levels = getLogLevels();
1366    while (levels.hasNext()) {
1367      result.addItem(levels.next());
1368    }
1369    result.setSelectedItem(_leastSevereDisplayedLogLevel);
1370
1371    result.addActionListener(new ActionListener() {
1372      public void actionPerformed(ActionEvent e) {
1373        JComboBox box = (JComboBox) e.getSource();
1374        LogLevel level = (LogLevel) box.getSelectedItem();
1375        setLeastSevereDisplayedLogLevel(level);
1376      }
1377    });
1378    result.setMaximumSize(result.getPreferredSize());
1379    return result;
1380  }
1381
1382  protected void setLeastSevereDisplayedLogLevel(LogLevel level) {
1383    if (level == null || _leastSevereDisplayedLogLevel == level) {
1384      return; // nothing to do
1385    }
1386    _leastSevereDisplayedLogLevel = level;
1387    _table.getFilteredLogTableModel().refresh();
1388    updateStatusLabel();
1389  }
1390
1391  /**
1392   * Ensures that the Table's ScrollPane Viewport will "track" with updates
1393   * to the Table.  When the vertical scroll bar is at its bottom anchor
1394   * and tracking is enabled then viewport will stay at the bottom most
1395   * point of the component.  The purpose of this feature is to allow
1396   * a developer to watch the table as messages arrive and not have to
1397   * scroll after each new message arrives.  When the vertical scroll bar
1398   * is at any other location, then no tracking will happen.
1399   * @deprecated tracking is now done automatically.
1400   */
1401  protected void trackTableScrollPane() {
1402    // do nothing
1403  }
1404
1405  protected void centerFrame(JFrame frame) {
1406    Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1407    Dimension comp = frame.getSize();
1408
1409    frame.setLocation(((screen.width - comp.width) / 2),
1410        ((screen.height - comp.height) / 2));
1411
1412  }
1413
1414  /**
1415   * Uses a JFileChooser to select a file to opened with the
1416   * LF5 GUI.
1417   */
1418  protected void requestOpen() {
1419    JFileChooser chooser;
1420
1421    if (_fileLocation == null) {
1422      chooser = new JFileChooser();
1423    } else {
1424      chooser = new JFileChooser(_fileLocation);
1425    }
1426
1427    int returnVal = chooser.showOpenDialog(_logMonitorFrame);
1428    if (returnVal == JFileChooser.APPROVE_OPTION) {
1429      File f = chooser.getSelectedFile();
1430      if (loadLogFile(f)) {
1431        _fileLocation = chooser.getSelectedFile();
1432        _mruFileManager.set(f);
1433        updateMRUList();
1434      }
1435    }
1436  }
1437
1438  /**
1439   * Uses a Dialog box to accept a URL to a file to be opened
1440   * with the LF5 GUI.
1441   */
1442  protected void requestOpenURL() {
1443    LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
1444        getBaseFrame(), "Open URL", "URL:");
1445    String temp = inputDialog.getText();
1446
1447    if (temp != null) {
1448      if (temp.indexOf("://") == -1) {
1449        temp = "http://" + temp;
1450      }
1451
1452      try {
1453        URL url = new URL(temp);
1454        if (loadLogFile(url)) {
1455          _mruFileManager.set(url);
1456          updateMRUList();
1457        }
1458      } catch (MalformedURLException e) {
1459        LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1460            getBaseFrame(), "Error reading URL.");
1461      }
1462    }
1463  }
1464
1465  /**
1466   * Removes old file list and creates a new file list
1467   * with the updated MRU list.
1468   */
1469  protected void updateMRUList() {
1470    JMenu menu = _logMonitorFrame.getJMenuBar().getMenu(0);
1471    menu.removeAll();
1472    menu.add(createOpenMI());
1473    menu.add(createOpenURLMI());
1474    menu.addSeparator();
1475    menu.add(createCloseMI());
1476    createMRUFileListMI(menu);
1477    menu.addSeparator();
1478    menu.add(createExitMI());
1479  }
1480
1481  protected void requestClose() {
1482    setCallSystemExitOnClose(false);
1483    closeAfterConfirm();
1484  }
1485
1486  /**
1487   * Opens a file in the MRU list.
1488   */
1489  protected void requestOpenMRU(ActionEvent e) {
1490    String file = e.getActionCommand();
1491    StringTokenizer st = new StringTokenizer(file);
1492    String num = st.nextToken().trim();
1493    file = st.nextToken("\n");
1494
1495    try {
1496      int index = Integer.parseInt(num) - 1;
1497
1498      InputStream in = _mruFileManager.getInputStream(index);
1499      LogFileParser lfp = new LogFileParser(in);
1500      lfp.parse(this);
1501
1502      _mruFileManager.moveToTop(index);
1503      updateMRUList();
1504
1505    } catch (Exception me) {
1506      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1507          getBaseFrame(), "Unable to load file " + file);
1508    }
1509
1510  }
1511
1512  protected void requestExit() {
1513    _mruFileManager.save();
1514    setCallSystemExitOnClose(true);
1515    closeAfterConfirm();
1516  }
1517
1518  protected void closeAfterConfirm() {
1519    StringBuffer message = new StringBuffer();
1520
1521    if (_callSystemExitOnClose == false) {
1522      message.append("Are you sure you want to close the logging ");
1523      message.append("console?\n");
1524      message.append("(Note: This will not shut down the Virtual Machine,\n");
1525      message.append("or the Swing event thread.)");
1526    } else {
1527      message.append("Are you sure you want to exit?\n");
1528      message.append("This will shut down the Virtual Machine.\n");
1529    }
1530
1531    String title =
1532        "Are you sure you want to dispose of the Logging Console?";
1533
1534    if (_callSystemExitOnClose == true) {
1535      title = "Are you sure you want to exit?";
1536    }
1537    int value = JOptionPane.showConfirmDialog(
1538        _logMonitorFrame,
1539        message.toString(),
1540        title,
1541        JOptionPane.OK_CANCEL_OPTION,
1542        JOptionPane.QUESTION_MESSAGE,
1543        null
1544    );
1545
1546    if (value == JOptionPane.OK_OPTION) {
1547      dispose();
1548    }
1549  }
1550
1551  protected Iterator getLogLevels() {
1552    return _levels.iterator();
1553  }
1554
1555  protected Iterator getLogTableColumns() {
1556    return _columns.iterator();
1557  }
1558
1559  /**
1560   * Loads and parses a log file.
1561   */
1562  protected boolean loadLogFile(File file) {
1563    boolean ok = false;
1564    try {
1565      LogFileParser lfp = new LogFileParser(file);
1566      lfp.parse(this);
1567      ok = true;
1568    } catch (IOException e) {
1569      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1570          getBaseFrame(), "Error reading " + file.getName());
1571    }
1572
1573    return ok;
1574  }
1575
1576  /**
1577   * Loads a parses a log file running on a server.
1578   */
1579  protected boolean loadLogFile(URL url) {
1580    boolean ok = false;
1581    try {
1582      LogFileParser lfp = new LogFileParser(url.openStream());
1583      lfp.parse(this);
1584      ok = true;
1585    } catch (IOException e) {
1586      LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
1587          getBaseFrame(), "Error reading URL:" + url.getFile());
1588    }
1589    return ok;
1590  }
1591  //--------------------------------------------------------------------------
1592  //   Private Methods:
1593  //--------------------------------------------------------------------------
1594
1595  //--------------------------------------------------------------------------
1596  //   Nested Top-Level Classes or Interfaces:
1597  //--------------------------------------------------------------------------
1598
1599  class LogBrokerMonitorWindowAdaptor extends WindowAdapter {
1600    protected LogBrokerMonitor _monitor;
1601
1602    public LogBrokerMonitorWindowAdaptor(LogBrokerMonitor monitor) {
1603      _monitor = monitor;
1604    }
1605
1606    public void windowClosing(WindowEvent ev) {
1607      _monitor.requestClose();
1608    }
1609  }
1610}
1611
1612