1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.chainsaw;
19
20 import java.awt.BorderLayout;
21 import java.awt.Component;
22 import java.awt.Container;
23 import java.awt.Dimension;
24 import java.awt.FlowLayout;
25 import java.awt.Font;
26 import java.awt.FontMetrics;
27 import java.awt.Point;
28 import java.awt.Toolkit;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.FocusEvent;
32 import java.awt.event.FocusListener;
33 import java.awt.event.InputEvent;
34 import java.awt.event.KeyEvent;
35 import java.awt.event.KeyListener;
36 import java.awt.event.MouseAdapter;
37 import java.awt.event.MouseEvent;
38 import java.awt.event.MouseMotionAdapter;
39 import java.awt.event.WindowAdapter;
40 import java.awt.event.WindowEvent;
41 import java.beans.PropertyChangeEvent;
42 import java.beans.PropertyChangeListener;
43 import java.io.BufferedInputStream;
44 import java.io.BufferedOutputStream;
45 import java.io.EOFException;
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.FileOutputStream;
50 import java.io.FileReader;
51 import java.io.FileWriter;
52 import java.io.IOException;
53 import java.io.ObjectInputStream;
54 import java.io.ObjectOutputStream;
55 import java.io.StringReader;
56 import java.net.URLEncoder;
57 import java.text.DateFormat;
58 import java.text.NumberFormat;
59 import java.text.SimpleDateFormat;
60 import java.util.ArrayList;
61 import java.util.Date;
62 import java.util.Enumeration;
63 import java.util.HashMap;
64 import java.util.HashSet;
65 import java.util.Iterator;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.StringTokenizer;
69 import java.util.Vector;
70
71 import javax.swing.AbstractAction;
72 import javax.swing.Action;
73 import javax.swing.BorderFactory;
74 import javax.swing.Box;
75 import javax.swing.BoxLayout;
76 import javax.swing.ButtonGroup;
77 import javax.swing.ImageIcon;
78 import javax.swing.JButton;
79 import javax.swing.JCheckBoxMenuItem;
80 import javax.swing.JComboBox;
81 import javax.swing.JComponent;
82 import javax.swing.JDialog;
83 import javax.swing.JEditorPane;
84 import javax.swing.JFrame;
85 import javax.swing.JLabel;
86 import javax.swing.JMenuItem;
87 import javax.swing.JPanel;
88 import javax.swing.JPopupMenu;
89 import javax.swing.JRadioButtonMenuItem;
90 import javax.swing.JScrollPane;
91 import javax.swing.JSeparator;
92 import javax.swing.JSplitPane;
93 import javax.swing.JTable;
94 import javax.swing.JTextArea;
95 import javax.swing.JTextField;
96 import javax.swing.JToolBar;
97 import javax.swing.KeyStroke;
98 import javax.swing.ListSelectionModel;
99 import javax.swing.SwingConstants;
100 import javax.swing.SwingUtilities;
101 import javax.swing.WindowConstants;
102 import javax.swing.event.ChangeEvent;
103 import javax.swing.event.DocumentEvent;
104 import javax.swing.event.DocumentListener;
105 import javax.swing.event.ListSelectionEvent;
106 import javax.swing.event.ListSelectionListener;
107 import javax.swing.event.TableColumnModelEvent;
108 import javax.swing.event.TableColumnModelListener;
109 import javax.swing.event.TableModelEvent;
110 import javax.swing.event.TableModelListener;
111 import javax.swing.table.TableColumn;
112 import javax.swing.table.TableColumnModel;
113 import javax.swing.text.Document;
114
115 import org.apache.log4j.LogManager;
116 import org.apache.log4j.Logger;
117 import org.apache.log4j.PatternLayout;
118 import org.apache.log4j.chainsaw.color.ColorPanel;
119 import org.apache.log4j.chainsaw.color.RuleColorizer;
120 import org.apache.log4j.chainsaw.filter.FilterModel;
121 import org.apache.log4j.chainsaw.icons.ChainsawIcons;
122 import org.apache.log4j.chainsaw.icons.LineIconFactory;
123 import org.apache.log4j.chainsaw.layout.DefaultLayoutFactory;
124 import org.apache.log4j.chainsaw.layout.EventDetailLayout;
125 import org.apache.log4j.chainsaw.layout.LayoutEditorPane;
126 import org.apache.log4j.chainsaw.messages.MessageCenter;
127 import org.apache.log4j.chainsaw.prefs.LoadSettingsEvent;
128 import org.apache.log4j.chainsaw.prefs.Profileable;
129 import org.apache.log4j.chainsaw.prefs.SaveSettingsEvent;
130 import org.apache.log4j.chainsaw.prefs.SettingsManager;
131 import org.apache.log4j.chainsaw.xstream.TableColumnConverter;
132 import org.apache.log4j.helpers.Constants;
133 import org.apache.log4j.rule.ExpressionRule;
134 import org.apache.log4j.rule.Rule;
135 import org.apache.log4j.spi.LoggingEvent;
136 import org.apache.log4j.spi.LoggingEventFieldResolver;
137
138 import com.thoughtworks.xstream.XStream;
139 import com.thoughtworks.xstream.io.xml.DomDriver;
140
141
142 /***
143 * A LogPanel provides a view to a collection of LoggingEvents.<br>
144 * <br>
145 * As events are received, the keywords in the 'tab identifier' application
146 * preference are replaced with the values from the received event. The
147 * main application uses this expression to route received LoggingEvents to
148 * individual LogPanels which match each event's resolved expression.<br>
149 * <br>
150 * The LogPanel's capabilities can be broken up into four areas:<br>
151 * <ul><li> toolbar - provides 'find' and 'refine focus' features
152 * <li> logger tree - displays a tree of the logger hierarchy, which can be used
153 * to filter the display
154 * <li> table - displays the events which pass the filtering rules
155 * <li>detail panel - displays information about the currently selected event
156 * </ul>
157 * Here is a complete list of LogPanel's capabilities:<br>
158 * <ul><li>display selected LoggingEvent row number and total LoggingEvent count
159 * <li>pause or unpause reception of LoggingEvents
160 * <li>configure, load and save column settings (displayed columns, order, width)
161 * <li>configure, load and save color rules
162 * filter displayed LoggingEvents based on the logger tree settings
163 * <li>filter displayed LoggingEvents based on a 'refine focus' expression
164 * (evaluates only those LoggingEvents which pass the logger tree filter
165 * <li>colorize LoggingEvents based on expressions
166 * <li>hide, show and configure the detail pane and tooltip
167 * <li>configure the formatting of the logger, level and timestamp fields
168 * <li>dock or undock
169 * <li>table displays first line of exception, but when cell is clicked, a
170 * popup opens to display the full stack trace
171 * <li>find
172 * <li>scroll to bottom
173 * <li>sort
174 * <li>provide a context menu which can be used to build color or display expressions
175 * <li>hide or show the logger tree
176 * <li>toggle the container storing the LoggingEvents to use either a
177 * CyclicBuffer (defaults to max size of 5000, but configurable through
178 * CHAINSAW_CAPACITY system property) or ArrayList (no max size)
179 * <li>use the mouse context menu to 'best-fit' columns, define display
180 * expression filters based on mouse location and access other capabilities
181 *</ul>
182 *
183 *@see org.apache.log4j.chainsaw.color.ColorPanel
184 *@see org.apache.log4j.rule.ExpressionRule
185 *@see org.apache.log4j.spi.LoggingEventFieldResolver
186 *
187 *@author Scott Deboy (sdeboy at apache.org)
188 *@author Paul Smith (psmith at apache.org)
189 *@author Stephen Pain
190 *@author Isuru Suriarachchi
191 *
192 */
193 public class LogPanel extends DockablePanel implements EventBatchListener,
194 Profileable {
195 private static final double DEFAULT_DETAIL_SPLIT_LOCATION = .5;
196 private static final double DEFAULT_LOG_TREE_SPLIT_LOCATION = .25;
197 private final String identifier;
198 private final ChainsawStatusBar statusBar;
199 private final JFrame preferencesFrame = new JFrame();
200 private final JFrame colorFrame = new JFrame();
201 private final JFrame undockedFrame;
202 private final DockablePanel externalPanel;
203 private final Action dockingAction;
204 private final JToolBar undockedToolbar;
205 private final JSortTable table;
206 private final TableColorizingRenderer renderer;
207 private final EventContainer tableModel;
208 private final ThrowableRenderPanel throwableRenderPanel;
209 private final JEditorPane detail;
210 private final JSplitPane lowerPanel;
211 private final DetailPaneUpdater detailPaneUpdater;
212 private final JPanel detailPanel = new JPanel(new BorderLayout());
213 private final JSplitPane nameTreeAndMainPanelSplit;
214 private final LoggerNameTreePanel logTreePanel;
215 private final LogPanelPreferenceModel preferenceModel =
216 new LogPanelPreferenceModel();
217 private final LogPanelPreferencePanel preferencesPanel =
218 new LogPanelPreferencePanel(preferenceModel);
219 private final FilterModel filterModel = new FilterModel();
220 private final RuleColorizer colorizer = new RuleColorizer();
221 private final RuleMediator ruleMediator = new RuleMediator();
222 private EventDetailLayout detailLayout = new EventDetailLayout();
223 private double lastDetailPanelSplitLocation = DEFAULT_DETAIL_SPLIT_LOCATION;
224 private double lastLogTreePanelSplitLocation =
225 DEFAULT_LOG_TREE_SPLIT_LOCATION;
226 private Point currentPoint;
227 private boolean paused = false;
228 private Rule findRule;
229 private final JPanel findPanel;
230 private JTextField findField;
231 private int dividerSize;
232 static final String TABLE_COLUMN_ORDER = "table.columns.order";
233 static final String TABLE_COLUMN_WIDTHS = "table.columns.widths";
234 static final String COLUMNS_EXTENSION = ".columns";
235 static final String COLORS_EXTENSION = ".colors";
236 private static final int LOG_PANEL_SERIALIZATION_VERSION_NUMBER = 1;
237 private int previousLastIndex = -1;
238 private final DateFormat timestampExpressionFormat = new SimpleDateFormat(Constants.TIMESTAMP_RULE_FORMAT);
239 private final Logger logger = LogManager.getLogger(LogPanel.class);
240 private final Vector filterExpressionVector;
241
242 /***
243 * Creates a new LogPanel object. If a LogPanel with this identifier has
244 * been loaded previously, reload settings saved on last exit.
245 *
246 * @param statusBar shared status bar, provided by main application
247 * @param identifier used to load and save settings
248 */
249 public LogPanel(final ChainsawStatusBar statusBar, final String identifier, int cyclicBufferSize) {
250 this.identifier = identifier;
251 this.statusBar = statusBar;
252 logger.debug("creating logpanel for " + identifier);
253
254 setLayout(new BorderLayout());
255 findPanel = new JPanel();
256
257 final Map columnNameKeywordMap = new HashMap();
258 columnNameKeywordMap.put(
259 ChainsawConstants.CLASS_COL_NAME, LoggingEventFieldResolver.CLASS_FIELD);
260 columnNameKeywordMap.put(
261 ChainsawConstants.FILE_COL_NAME, LoggingEventFieldResolver.FILE_FIELD);
262 columnNameKeywordMap.put(
263 ChainsawConstants.LEVEL_COL_NAME, LoggingEventFieldResolver.LEVEL_FIELD);
264 columnNameKeywordMap.put(
265 ChainsawConstants.LINE_COL_NAME, LoggingEventFieldResolver.LINE_FIELD);
266 columnNameKeywordMap.put(
267 ChainsawConstants.LOGGER_COL_NAME, LoggingEventFieldResolver.LOGGER_FIELD);
268 columnNameKeywordMap.put(
269 ChainsawConstants.NDC_COL_NAME, LoggingEventFieldResolver.NDC_FIELD);
270 columnNameKeywordMap.put(
271 ChainsawConstants.MESSAGE_COL_NAME, LoggingEventFieldResolver.MSG_FIELD);
272 columnNameKeywordMap.put(
273 ChainsawConstants.THREAD_COL_NAME, LoggingEventFieldResolver.THREAD_FIELD);
274 columnNameKeywordMap.put(
275 ChainsawConstants.THROWABLE_COL_NAME,
276 LoggingEventFieldResolver.EXCEPTION_FIELD);
277 columnNameKeywordMap.put(
278 ChainsawConstants.TIMESTAMP_COL_NAME,
279 LoggingEventFieldResolver.TIMESTAMP_FIELD);
280
281 preferencesFrame.setTitle("'" + identifier + "' Log Panel Preferences");
282 preferencesFrame.setIconImage(
283 ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
284 preferencesFrame.getContentPane().add(preferencesPanel);
285
286 preferencesFrame.setSize(640, 480);
287
288 preferencesPanel.setOkCancelActionListener(
289 new ActionListener() {
290 public void actionPerformed(ActionEvent e) {
291 preferencesFrame.setVisible(false);
292 }
293 });
294
295 setDetailPaneConversionPattern(
296 DefaultLayoutFactory.getDefaultPatternLayout());
297 detailLayout.setConversionPattern(
298 DefaultLayoutFactory.getDefaultPatternLayout());
299
300 undockedFrame = new JFrame(identifier);
301 undockedFrame.setDefaultCloseOperation(
302 WindowConstants.DO_NOTHING_ON_CLOSE);
303
304 if (ChainsawIcons.UNDOCKED_ICON != null) {
305 undockedFrame.setIconImage(
306 new ImageIcon(ChainsawIcons.UNDOCKED_ICON).getImage());
307 }
308
309 externalPanel = new DockablePanel();
310 externalPanel.setLayout(new BorderLayout());
311 undockedFrame.getContentPane().add(externalPanel);
312
313 undockedFrame.addWindowListener(
314 new WindowAdapter() {
315 public void windowClosing(WindowEvent e) {
316 dock();
317 }
318 });
319
320 undockedToolbar = createDockwindowToolbar();
321 externalPanel.add(undockedToolbar, BorderLayout.NORTH);
322 undockedFrame.pack();
323
324
325
326
327
328 /***
329 * Setup a popup menu triggered for Timestamp column to allow time stamp
330 * format changes
331 */
332 final JPopupMenu dateFormatChangePopup = new JPopupMenu();
333 final JRadioButtonMenuItem isoButton =
334 new JRadioButtonMenuItem(
335 new AbstractAction("Use ISO8601Format") {
336 public void actionPerformed(ActionEvent e) {
337 preferenceModel.setDateFormatPattern("ISO8601");
338 }
339 });
340 final JRadioButtonMenuItem simpleTimeButton =
341 new JRadioButtonMenuItem(
342 new AbstractAction("Use simple time") {
343 public void actionPerformed(ActionEvent e) {
344 preferenceModel.setDateFormatPattern("HH:mm:ss");
345 }
346 });
347
348 ButtonGroup dfBG = new ButtonGroup();
349 dfBG.add(isoButton);
350 dfBG.add(simpleTimeButton);
351 isoButton.setSelected(true);
352 dateFormatChangePopup.add(isoButton);
353 dateFormatChangePopup.add(simpleTimeButton);
354
355 final JCheckBoxMenuItem menuItemToggleToolTips =
356 new JCheckBoxMenuItem("Show ToolTips");
357 menuItemToggleToolTips.addActionListener(
358 new ActionListener() {
359 public void actionPerformed(ActionEvent evt) {
360 preferenceModel.setToolTips(menuItemToggleToolTips.isSelected());
361 }
362 });
363 menuItemToggleToolTips.setIcon(new ImageIcon(ChainsawIcons.TOOL_TIP));
364
365 final JCheckBoxMenuItem menuItemLoggerTree =
366 new JCheckBoxMenuItem("Show Logger Tree panel");
367 menuItemLoggerTree.addActionListener(
368 new ActionListener() {
369 public void actionPerformed(ActionEvent e) {
370 preferenceModel.setLogTreePanelVisible(
371 menuItemLoggerTree.isSelected());
372 }
373 });
374 menuItemLoggerTree.setIcon(new ImageIcon(ChainsawIcons.WINDOW_ICON));
375
376 final JCheckBoxMenuItem menuItemScrollBottom =
377 new JCheckBoxMenuItem("Scroll to bottom");
378 menuItemScrollBottom.addActionListener(
379 new ActionListener() {
380 public void actionPerformed(ActionEvent evt) {
381 preferenceModel.setScrollToBottom(menuItemScrollBottom.isSelected());
382 }
383 });
384 menuItemScrollBottom.setSelected(isScrollToBottom());
385
386 menuItemScrollBottom.setIcon(
387 new ImageIcon(ChainsawIcons.SCROLL_TO_BOTTOM));
388
389 final JCheckBoxMenuItem menuItemToggleDetails =
390 new JCheckBoxMenuItem("Show Detail Pane");
391 menuItemToggleDetails.addActionListener(
392 new ActionListener() {
393 public void actionPerformed(ActionEvent e) {
394 preferenceModel.setDetailPaneVisible(
395 menuItemToggleDetails.isSelected());
396 }
397 });
398
399 menuItemToggleDetails.setIcon(new ImageIcon(ChainsawIcons.INFO));
400
401
402
403
404 preferenceModel.addPropertyChangeListener(
405 "levelIcons",
406 new PropertyChangeListener() {
407 public void propertyChange(PropertyChangeEvent evt) {
408 renderer.setLevelUseIcons(
409 ((Boolean) evt.getNewValue()).booleanValue());
410 table.tableChanged(new TableModelEvent(tableModel));
411 }
412 });
413
414 preferenceModel.addPropertyChangeListener(
415 "detailPaneVisible",
416 new PropertyChangeListener() {
417 public void propertyChange(PropertyChangeEvent evt) {
418 boolean newValue = ((Boolean) evt.getNewValue()).booleanValue();
419
420 if (newValue) {
421 showDetailPane();
422 } else {
423 hideDetailPane();
424 }
425 }
426 });
427
428 preferenceModel.addPropertyChangeListener(
429 "logTreePanelVisible",
430 new PropertyChangeListener() {
431 public void propertyChange(PropertyChangeEvent evt) {
432 boolean newValue = ((Boolean) evt.getNewValue()).booleanValue();
433
434 if (newValue) {
435 showLogTreePanel();
436 } else {
437 hideLogTreePanel();
438 }
439 }
440 });
441
442 preferenceModel.addPropertyChangeListener(
443 "toolTips",
444 new PropertyChangeListener() {
445 public void propertyChange(PropertyChangeEvent evt) {
446 renderer.setToolTipsVisible(
447 ((Boolean) evt.getNewValue()).booleanValue());
448 }
449 });
450
451 preferenceModel.addPropertyChangeListener(
452 "visibleColumns",
453 new PropertyChangeListener() {
454 public void propertyChange(PropertyChangeEvent evt) {
455
456 TableColumnModel columnModel = table.getColumnModel();
457 while (columnModel.getColumnCount() > 0) {
458 columnModel.removeColumn(columnModel.getColumn(0));
459 }
460 for (Iterator iter = preferenceModel.getVisibleColumnOrder().iterator();iter.hasNext();) {
461 TableColumn c = (TableColumn)iter.next();
462 columnModel.addColumn(c);
463 }
464 }
465 });
466
467 PropertyChangeListener datePrefsChangeListener =
468 new PropertyChangeListener() {
469 public void propertyChange(PropertyChangeEvent evt) {
470 LogPanelPreferenceModel model =
471 (LogPanelPreferenceModel) evt.getSource();
472
473 isoButton.setSelected(model.isUseISO8601Format());
474 simpleTimeButton.setSelected(
475 !model.isUseISO8601Format() && !model.isCustomDateFormat());
476
477 if (model.isUseISO8601Format()) {
478 renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
479 } else {
480 try {
481 renderer.setDateFormatter(
482 new SimpleDateFormat(model.getDateFormatPattern()));
483 } catch (IllegalArgumentException iae) {
484 model.setDefaultDatePatternFormat();
485 renderer.setDateFormatter(new SimpleDateFormat(Constants.ISO8601_PATTERN));
486 }
487 }
488
489 table.tableChanged(new TableModelEvent(tableModel));
490 }
491 };
492
493 preferenceModel.addPropertyChangeListener(
494 "dateFormatPattern", datePrefsChangeListener);
495 preferenceModel.addPropertyChangeListener(
496 "dateFormatPattern", datePrefsChangeListener);
497
498 preferenceModel.addPropertyChangeListener(
499 "loggerPrecision",
500 new PropertyChangeListener() {
501 public void propertyChange(PropertyChangeEvent evt) {
502 LogPanelPreferenceModel model =
503 (LogPanelPreferenceModel) evt.getSource();
504
505 renderer.setLoggerPrecision(model.getLoggerPrecision());
506
507 table.tableChanged(new TableModelEvent(tableModel));
508 }
509 });
510
511 preferenceModel.addPropertyChangeListener(
512 "toolTips",
513 new PropertyChangeListener() {
514 public void propertyChange(PropertyChangeEvent evt) {
515 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
516 menuItemToggleToolTips.setSelected(value);
517 }
518 });
519
520 preferenceModel.addPropertyChangeListener(
521 "logTreePanelVisible",
522 new PropertyChangeListener() {
523 public void propertyChange(PropertyChangeEvent evt) {
524 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
525 menuItemLoggerTree.setSelected(value);
526 }
527 });
528
529 preferenceModel.addPropertyChangeListener(
530 "scrollToBottom",
531 new PropertyChangeListener() {
532 public void propertyChange(PropertyChangeEvent evt) {
533 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
534 menuItemScrollBottom.setSelected(value);
535 if (value) {
536 table.scrollToBottom(table.columnAtPoint(table.getVisibleRect().getLocation()));
537 }
538 }
539 });
540
541 preferenceModel.addPropertyChangeListener(
542 "detailPaneVisible",
543 new PropertyChangeListener() {
544 public void propertyChange(PropertyChangeEvent evt) {
545 boolean value = ((Boolean) evt.getNewValue()).booleanValue();
546 menuItemToggleDetails.setSelected(value);
547 }
548 });
549
550
551
552
553 tableModel = new ChainsawCyclicBufferTableModel(cyclicBufferSize);
554 table = new JSortTable(tableModel);
555
556 tableModel.addNewKeyListener(new NewKeyListener() {
557 public void newKeyAdded(NewKeyEvent e) {
558 columnNameKeywordMap.put(e.getKey(), "PROP." + e.getKey());
559 }
560 });
561
562
563
564
565
566 tableModel.setDisplayRule(ruleMediator);
567
568 tableModel.addEventCountListener(
569 new EventCountListener() {
570 public void eventCountChanged(int currentCount, int totalCount) {
571 if (LogPanel.this.isVisible()) {
572 statusBar.setSelectedLine(
573 table.getSelectedRow() + 1, currentCount, totalCount);
574 }
575 }
576 });
577
578 tableModel.addEventCountListener(
579 new EventCountListener() {
580 final NumberFormat formatter = NumberFormat.getPercentInstance();
581 boolean warning75 = false;
582 boolean warning100 = false;
583
584 public void eventCountChanged(int currentCount, int totalCount) {
585 if (tableModel.isCyclic()) {
586 double percent =
587 ((double) totalCount) / ((ChainsawCyclicBufferTableModel) tableModel)
588 .getMaxSize();
589 String msg = null;
590
591 if ((percent > 0.75) && (percent < 1.0) && !warning75) {
592 msg =
593 "Warning :: " + formatter.format(percent) + " of the '"
594 + getIdentifier() + "' buffer has been used";
595 warning75 = true;
596 } else if ((percent >= 1.0) && !warning100) {
597 msg =
598 "Warning :: " + formatter.format(percent) + " of the '"
599 + getIdentifier()
600 + "' buffer has been used. Older events are being discarded.";
601 warning100 = true;
602 }
603
604 if (msg != null) {
605 MessageCenter.getInstance().getLogger().info(msg);
606 }
607 }
608 }
609 });
610
611
612
613
614
615 LogPanelLoggerTreeModel logTreeModel = new LogPanelLoggerTreeModel();
616 logTreePanel = new LoggerNameTreePanel(logTreeModel, preferenceModel);
617 tableModel.addLoggerNameListener(logTreeModel);
618
619 /***
620 * Set the LoggerRule to be the LoggerTreePanel, as this visual component
621 * is a rule itself, and the RuleMediator will automatically listen when
622 * it's rule state changes.
623 */
624 ruleMediator.setLoggerRule(logTreePanel);
625 colorizer.setLoggerRule(logTreePanel.getLoggerColorRule());
626
627
628
629
630 colorFrame.setTitle("'" + identifier + "' Color Filter");
631 colorFrame.setIconImage(
632 ((ImageIcon) ChainsawIcons.ICON_PREFERENCES).getImage());
633
634 final ColorPanel colorPanel = new ColorPanel(colorizer, filterModel);
635
636 colorFrame.getContentPane().add(colorPanel);
637
638 colorPanel.setCloseActionListener(
639 new ActionListener() {
640 public void actionPerformed(ActionEvent e) {
641 colorFrame.setVisible(false);
642 }
643 });
644
645 colorizer.addPropertyChangeListener(
646 "colorrule",
647 new PropertyChangeListener() {
648 public void propertyChange(PropertyChangeEvent evt) {
649 if (table != null) {
650 table.repaint();
651 }
652 }
653 });
654
655
656
657
658 table.setRowHeight(20);
659 table.setShowGrid(false);
660
661 table.getColumnModel().addColumnModelListener(
662 new ChainsawTableColumnModelListener());
663
664 table.setAutoCreateColumnsFromModel(false);
665
666 table.addMouseMotionListener(new TableColumnDetailMouseListener());
667
668 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
669
670
671 table.addKeyListener(
672 new KeyListener() {
673 public void keyTyped(KeyEvent e) {
674 }
675
676 public void keyPressed(KeyEvent e) {
677 synchronized (detail) {
678 table.getSelectionModel().setValueIsAdjusting(true);
679 detail.notify();
680 }
681 }
682
683 public void keyReleased(KeyEvent e) {
684 synchronized (detail) {
685 table.getSelectionModel().setValueIsAdjusting(false);
686 detail.notify();
687 }
688 }
689 });
690
691 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
692
693 table.getSelectionModel().addListSelectionListener(
694 new ListSelectionListener() {
695 public void valueChanged(ListSelectionEvent evt) {
696 if (
697 ((evt.getFirstIndex() == evt.getLastIndex())
698 && (evt.getFirstIndex() > 0)) || (evt.getValueIsAdjusting())) {
699 return;
700 }
701 boolean lastIndexOnLastRow = (evt.getLastIndex() == (table.getRowCount() - 1));
702 boolean lastIndexSame = (previousLastIndex == evt.getLastIndex());
703
704
705
706
707
708
709
710
711
712
713
714
715 boolean disableScrollToBottom = (lastIndexOnLastRow && lastIndexSame && previousLastIndex != evt.getFirstIndex());
716 if (disableScrollToBottom && isScrollToBottom() && table.getRowCount() > 0) {
717 preferenceModel.setScrollToBottom(false);
718 }
719 previousLastIndex = evt.getLastIndex();
720
721 final ListSelectionModel lsm = (ListSelectionModel) evt.getSource();
722
723 if (lsm.isSelectionEmpty()) {
724 if (isVisible()) {
725 statusBar.setNothingSelected();
726 }
727
728 if (detail.getDocument().getDefaultRootElement() != null) {
729 detailPaneUpdater.setSelectedRow(-1);
730 }
731 } else {
732 if (table.getSelectedRow() > -1) {
733 int selectedRow = table.getSelectedRow();
734
735 if (isVisible()) {
736 updateStatusBar();
737 }
738
739 try {
740 if (tableModel.getRowCount() >= selectedRow) {
741 detailPaneUpdater.setSelectedRow(table.getSelectedRow());
742 } else {
743 detailPaneUpdater.setSelectedRow(-1);
744 }
745 } catch (Exception e) {
746 e.printStackTrace();
747 detailPaneUpdater.setSelectedRow(-1);
748 }
749 }
750 }
751 }
752 });
753
754 renderer = new TableColorizingRenderer(colorizer);
755 renderer.setToolTipsVisible(preferenceModel.isToolTips());
756
757 table.setDefaultRenderer(Object.class, renderer);
758
759
760
761
762 throwableRenderPanel = new ThrowableRenderPanel();
763
764 final JDialog detailDialog = new JDialog((JFrame) null, true);
765 Container container = detailDialog.getContentPane();
766 final JTextArea detailArea = new JTextArea(10, 40);
767 detailArea.setEditable(false);
768 container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS));
769 container.add(new JScrollPane(detailArea));
770
771 detailDialog.pack();
772
773 throwableRenderPanel.addActionListener(
774 new ActionListener() {
775 public void actionPerformed(ActionEvent e) {
776 Object o = table.getValueAt(
777 table.getSelectedRow(), table.getSelectedColumn());
778 if (o == null) {
779
780 logger.debug("no row selected - unable to display throwable popup");
781 return;
782 }
783 detailDialog.setTitle(
784 table.getColumnName(table.getSelectedColumn()) + " detail...");
785
786 if (o instanceof String[]) {
787 StringBuffer buf = new StringBuffer();
788 String[] ti = (String[]) o;
789 buf.append(ti[0]).append("\n");
790
791 for (int i = 1; i < ti.length; i++) {
792 buf.append(ti[i]).append("\n ");
793 }
794
795 detailArea.setText(buf.toString());
796 } else {
797 detailArea.setText((o == null) ? "" : o.toString());
798 }
799
800 detailDialog.setLocation(lowerPanel.getLocationOnScreen());
801 SwingUtilities.invokeLater(
802 new Runnable() {
803 public void run() {
804 detailDialog.setVisible(true);
805 }
806 });
807 }
808 });
809
810
811
812
813
814 tableModel.addNewKeyListener(
815 new NewKeyListener() {
816 public void newKeyAdded(final NewKeyEvent e) {
817 SwingUtilities.invokeLater(new Runnable() {
818 public void run() {
819
820
821
822
823 try {
824 if(table.getColumn(e.getKey())!=null){
825 return;
826 }
827 } catch (IllegalArgumentException iae) {}
828 TableColumn col = new TableColumn(e.getNewModelIndex());
829 col.setHeaderValue(e.getKey());
830
831 if (preferenceModel.addColumn(col)) {
832 table.addColumn(col);
833 preferenceModel.setColumnVisible(e.getKey().toString(), true);
834 }
835 }
836 });
837 }
838 });
839
840 tableModel.addPropertyChangeListener(
841 "cyclic",
842 new PropertyChangeListener() {
843 public void propertyChange(PropertyChangeEvent arg0) {
844 if (tableModel.isCyclic()) {
845 MessageCenter.getInstance().getLogger().warn(
846 "Changed to Cyclic Mode. Maximum # events kept: "
847 + tableModel.getMaxSize());
848 } else {
849 MessageCenter.getInstance().getLogger().warn(
850 "Changed to Unlimited Mode. Warning, you may run out of memory.");
851 }
852 }
853 });
854
855 table.getTableHeader().addMouseListener(
856 new MouseAdapter() {
857 public void mouseClicked(MouseEvent e) {
858 checkEvent(e);
859 }
860
861 public void mousePressed(MouseEvent e) {
862 checkEvent(e);
863 }
864
865 public void mouseReleased(MouseEvent e) {
866 checkEvent(e);
867 }
868
869 private void checkEvent(MouseEvent e) {
870 if (e.isPopupTrigger()) {
871 TableColumnModel colModel = table.getColumnModel();
872 int index = colModel.getColumnIndexAtX(e.getX());
873 int modelIndex = colModel.getColumn(index).getModelIndex();
874
875 if ((modelIndex + 1) == ChainsawColumns.INDEX_TIMESTAMP_COL_NAME) {
876 dateFormatChangePopup.show(e.getComponent(), e.getX(), e.getY());
877 }
878 }
879 }
880 });
881
882
883
884
885 JPanel upperPanel = new JPanel(new BorderLayout());
886 upperPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 2, 0));
887
888 final JLabel filterLabel = new JLabel("Refine focus on: ");
889 filterLabel.setFont(filterLabel.getFont().deriveFont(Font.BOLD));
890
891 JPanel upperLeftPanel =
892 new JPanel(new FlowLayout(FlowLayout.CENTER, 3, 0));
893 upperLeftPanel.add(filterLabel);
894
895
896
897 filterExpressionVector = new Vector();
898
899 filterExpressionVector.add("LEVEL == TRACE");
900 filterExpressionVector.add("LEVEL >= DEBUG");
901 filterExpressionVector.add("LEVEL >= INFO");
902 filterExpressionVector.add("LEVEL >= WARN");
903 filterExpressionVector.add("LEVEL >= ERROR");
904 filterExpressionVector.add("LEVEL == FATAL");
905
906 final JComboBox filterCombo = new JComboBox(filterExpressionVector);
907 filterCombo.setSelectedIndex(-1);
908 final JTextField filterText;
909
910 if (filterCombo.getEditor().getEditorComponent() instanceof JTextField) {
911 String comboToolTipText =
912 "Enter an expression, press enter to add to list";
913 filterText = (JTextField) filterCombo.getEditor().getEditorComponent();
914 filterText.setToolTipText(comboToolTipText);
915 filterText.addKeyListener(
916 new ExpressionRuleContext(filterModel, filterText));
917 filterText.getDocument().addDocumentListener(
918 new DelayedFilterTextDocumentListener(filterText));
919 filterCombo.setEditable(true);
920 filterCombo.addActionListener(
921 new AbstractAction() {
922 public void actionPerformed(ActionEvent e) {
923 if (e.getActionCommand().equals("comboBoxEdited")) {
924 try {
925
926 ExpressionRule.getRule(
927 filterCombo.getSelectedItem().toString());
928 } catch (IllegalArgumentException iae) {
929
930 return;
931 }
932
933
934 if (!(filterExpressionVector.contains(filterCombo.getSelectedItem()))) {
935 filterCombo.addItem(filterCombo.getSelectedItem());
936 }
937 }
938 }
939 });
940 upperPanel.add(filterCombo, BorderLayout.CENTER);
941 } else {
942 filterText = new JTextField();
943 filterText.setToolTipText("Enter an expression");
944 filterText.addKeyListener(
945 new ExpressionRuleContext(filterModel, filterText));
946 filterText.getDocument().addDocumentListener(
947 new DelayedFilterTextDocumentListener(filterText));
948 upperPanel.add(filterText, BorderLayout.CENTER);
949 }
950
951 upperPanel.add(upperLeftPanel, BorderLayout.WEST);
952
953 JPanel upperRightPanel =
954 new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
955
956
957 final JButton clearButton = new JButton("Clear expression");
958 clearButton.setToolTipText("Click here to remove the selected expression from the list");
959 clearButton.addActionListener(
960 new AbstractAction() {
961 public void actionPerformed(ActionEvent e){
962 Object selectedItem = filterCombo.getSelectedItem();
963 if (e.getSource() == clearButton && selectedItem != null && !selectedItem.toString().equals("")){
964 if (filterExpressionVector.contains(selectedItem.toString())){
965 filterExpressionVector.remove(selectedItem.toString());
966 }
967 filterCombo.setSelectedIndex(-1);
968 }
969
970 filterText.setText(null);
971 }
972 }
973 );
974
975 upperRightPanel.add(clearButton);
976
977 upperPanel.add(upperRightPanel, BorderLayout.EAST);
978
979
980
981
982 detail = new JEditorPane(ChainsawConstants.DETAIL_CONTENT_TYPE, "");
983 detail.setEditable(false);
984
985 detailPaneUpdater = new DetailPaneUpdater();
986
987 addFocusListener(new FocusListener() {
988
989 public void focusGained(FocusEvent e) {
990 detailPaneUpdater.updateDetailPane();
991 }
992
993 public void focusLost(FocusEvent e) {
994
995 }
996 });
997
998 tableModel.addTableModelListener(new TableModelListener() {
999 public void tableChanged(TableModelEvent e) {
1000 detailPaneUpdater.setSelectedRow(table.getSelectedRow());
1001 }
1002 });
1003
1004 addPropertyChangeListener(
1005 "detailPaneConversionPattern", detailPaneUpdater);
1006
1007 final JScrollPane detailPane = new JScrollPane(detail);
1008
1009 detailPane.setPreferredSize(new Dimension(900, 50));
1010
1011 detailPanel.add(detailPane, BorderLayout.CENTER);
1012
1013 JPanel eventsAndStatusPanel = new JPanel(new BorderLayout());
1014
1015 final JScrollPane eventsPane = new JScrollPane(table);
1016
1017 eventsAndStatusPanel.add(eventsPane, BorderLayout.CENTER);
1018
1019 final JPanel statusLabelPanel = new JPanel();
1020 statusLabelPanel.setLayout(new BorderLayout());
1021
1022 statusLabelPanel.add(upperPanel, BorderLayout.CENTER);
1023 eventsAndStatusPanel.add(statusLabelPanel, BorderLayout.NORTH);
1024
1025 lowerPanel =
1026 new JSplitPane(
1027 JSplitPane.VERTICAL_SPLIT, eventsAndStatusPanel, detailPanel);
1028
1029 dividerSize = lowerPanel.getDividerSize();
1030 lowerPanel.setDividerLocation(-1);
1031
1032 lowerPanel.setResizeWeight(1.0);
1033 lowerPanel.setBorder(null);
1034 lowerPanel.setContinuousLayout(true);
1035
1036 if (preferenceModel.isDetailPaneVisible()) {
1037 showDetailPane();
1038 } else {
1039 hideDetailPane();
1040 }
1041
1042
1043
1044
1045 final JToolBar detailToolbar = new JToolBar(SwingConstants.HORIZONTAL);
1046 detailToolbar.setFloatable(false);
1047
1048 final LayoutEditorPane layoutEditorPane = new LayoutEditorPane();
1049 final JDialog layoutEditorDialog =
1050 new JDialog((JFrame) null, "Pattern Editor");
1051 layoutEditorDialog.getContentPane().add(layoutEditorPane);
1052 layoutEditorDialog.setSize(640, 480);
1053
1054 layoutEditorPane.addCancelActionListener(
1055 new ActionListener() {
1056 public void actionPerformed(ActionEvent e) {
1057 layoutEditorDialog.setVisible(false);
1058 }
1059 });
1060
1061 layoutEditorPane.addOkActionListener(
1062 new ActionListener() {
1063 public void actionPerformed(ActionEvent e) {
1064 setDetailPaneConversionPattern(
1065 layoutEditorPane.getConversionPattern());
1066 layoutEditorDialog.setVisible(false);
1067 }
1068 });
1069
1070 Action editDetailAction =
1071 new AbstractAction(
1072 "Edit...", new ImageIcon(ChainsawIcons.ICON_EDIT_RECEIVER)) {
1073 public void actionPerformed(ActionEvent e) {
1074 layoutEditorPane.setConversionPattern(
1075 getDetailPaneConversionPattern());
1076
1077 Dimension size = Toolkit.getDefaultToolkit().getScreenSize();
1078 Point p =
1079 new Point(
1080 ((int) ((size.getWidth() / 2)
1081 - (layoutEditorDialog.getSize().getWidth() / 2))),
1082 ((int) ((size.getHeight() / 2)
1083 - (layoutEditorDialog.getSize().getHeight() / 2))));
1084 layoutEditorDialog.setLocation(p);
1085
1086 layoutEditorDialog.setVisible(true);
1087 }
1088 };
1089
1090 editDetailAction.putValue(
1091 Action.SHORT_DESCRIPTION,
1092 "opens a Dialog window to Edit the Pattern Layout text");
1093
1094 final SmallButton editDetailButton = new SmallButton(editDetailAction);
1095 editDetailButton.setText(null);
1096 detailToolbar.add(Box.createHorizontalGlue());
1097 detailToolbar.add(editDetailButton);
1098 detailToolbar.addSeparator();
1099 detailToolbar.add(Box.createHorizontalStrut(5));
1100
1101 Action closeDetailAction =
1102 new AbstractAction(null, LineIconFactory.createCloseIcon()) {
1103 public void actionPerformed(ActionEvent arg0) {
1104 preferenceModel.setDetailPaneVisible(false);
1105 }
1106 };
1107
1108 closeDetailAction.putValue(
1109 Action.SHORT_DESCRIPTION, "Hides the Detail Panel");
1110
1111 SmallButton closeDetailButton = new SmallButton(closeDetailAction);
1112 detailToolbar.add(closeDetailButton);
1113
1114 detailPanel.add(detailToolbar, BorderLayout.NORTH);
1115
1116 JPopupMenu editDetailPopupMenu = new JPopupMenu();
1117 editDetailPopupMenu.add(editDetailAction);
1118 editDetailPopupMenu.addSeparator();
1119
1120 final ButtonGroup layoutGroup = new ButtonGroup();
1121
1122 JRadioButtonMenuItem defaultLayoutRadio =
1123 new JRadioButtonMenuItem(
1124 new AbstractAction("Set to Default Layout") {
1125 public void actionPerformed(ActionEvent e) {
1126 setDetailPaneConversionPattern(
1127 DefaultLayoutFactory.getDefaultPatternLayout());
1128 }
1129 });
1130 editDetailPopupMenu.add(defaultLayoutRadio);
1131 layoutGroup.add(defaultLayoutRadio);
1132 defaultLayoutRadio.setSelected(true);
1133
1134 JRadioButtonMenuItem tccLayoutRadio =
1135 new JRadioButtonMenuItem(
1136 new AbstractAction("Set to TCCLayout") {
1137 public void actionPerformed(ActionEvent e) {
1138 setDetailPaneConversionPattern(
1139 PatternLayout.TTCC_CONVERSION_PATTERN);
1140 }
1141 });
1142 editDetailPopupMenu.add(tccLayoutRadio);
1143 layoutGroup.add(tccLayoutRadio);
1144
1145 PopupListener editDetailPopupListener =
1146 new PopupListener(editDetailPopupMenu);
1147 detail.addMouseListener(editDetailPopupListener);
1148
1149
1150
1151
1152 nameTreeAndMainPanelSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, logTreePanel, lowerPanel);
1153
1154 nameTreeAndMainPanelSplit.setToolTipText("Still under development....");
1155 nameTreeAndMainPanelSplit.setDividerLocation(-1);
1156
1157 add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
1158
1159 if (isLogTreeVisible()) {
1160 showLogTreePanel();
1161 } else {
1162 hideLogTreePanel();
1163 }
1164
1165
1166
1167
1168 final JMenuItem menuItemBestFit = new JMenuItem("Best fit column");
1169 menuItemBestFit.addActionListener(
1170 new ActionListener() {
1171 public void actionPerformed(ActionEvent evt) {
1172 if (currentPoint != null) {
1173 int column = table.columnAtPoint(currentPoint);
1174 int maxWidth = getMaxColumnWidth(column);
1175 table.getColumnModel().getColumn(column).setPreferredWidth(
1176 maxWidth);
1177 }
1178 }
1179 });
1180
1181 JMenuItem menuItemColorPanel = new JMenuItem("LogPanel Color Filter...");
1182 menuItemColorPanel.addActionListener(
1183 new ActionListener() {
1184 public void actionPerformed(ActionEvent evt) {
1185 showColorPreferences();
1186 }
1187 });
1188 menuItemColorPanel.setIcon(ChainsawIcons.ICON_PREFERENCES);
1189
1190 JMenuItem menuItemLogPanelPreferences =
1191 new JMenuItem("LogPanel Preferences...");
1192 menuItemLogPanelPreferences.addActionListener(
1193 new ActionListener() {
1194 public void actionPerformed(ActionEvent evt) {
1195 showPreferences();
1196 }
1197 });
1198 menuItemLogPanelPreferences.setIcon(ChainsawIcons.ICON_PREFERENCES);
1199
1200 final JMenuItem menuItemFocusOn =
1201 new JMenuItem("Set 'refine focus' field");
1202 menuItemFocusOn.addActionListener(
1203 new ActionListener() {
1204 public void actionPerformed(ActionEvent evt) {
1205 if (currentPoint != null) {
1206 String operator = "==";
1207 int column = table.columnAtPoint(currentPoint);
1208 int row = table.rowAtPoint(currentPoint);
1209 String colName = table.getColumnName(column);
1210 String value = "";
1211
1212 if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
1213 value = timestampExpressionFormat.format(new Date(table.getValueAt(row, column).toString()));
1214 } else {
1215 Object o = table.getValueAt(row, column);
1216
1217 if (o != null) {
1218 if (o instanceof String[]) {
1219 value = ((String[]) o)[0];
1220 operator = "~=";
1221 } else {
1222 value = o.toString();
1223 }
1224 }
1225 }
1226
1227 if (columnNameKeywordMap.containsKey(colName)) {
1228 filterText.setText(
1229 columnNameKeywordMap.get(colName).toString() + " " + operator
1230 + " '" + value + "'");
1231 }
1232 }
1233 }
1234 });
1235
1236 final JMenuItem menuDefineAddCustomFilter =
1237 new JMenuItem("Add to 'refine focus' field");
1238 menuDefineAddCustomFilter.addActionListener(
1239 new ActionListener() {
1240 public void actionPerformed(ActionEvent evt) {
1241 if (currentPoint != null) {
1242 String operator = "==";
1243 int column = table.columnAtPoint(currentPoint);
1244 int row = table.rowAtPoint(currentPoint);
1245 String colName = table.getColumnName(column);
1246 String value = "";
1247
1248 if (colName.equalsIgnoreCase(ChainsawConstants.TIMESTAMP_COL_NAME)) {
1249 JComponent comp =
1250 (JComponent) table.getCellRenderer(row, column);
1251
1252 if (comp instanceof JLabel) {
1253 value = ((JLabel) comp).getText();
1254 }
1255 } else {
1256 Object o = table.getValueAt(row, column).toString();
1257
1258 if (o instanceof String[]) {
1259 value = ((String[]) o)[0];
1260 operator = "~=";
1261 } else {
1262 value = o.toString();
1263 }
1264 }
1265
1266 if (columnNameKeywordMap.containsKey(colName)) {
1267 filterText.setText(
1268 filterText.getText() + " && "
1269 + columnNameKeywordMap.get(colName).toString() + " "
1270 + operator + " '" + value + "'");
1271 }
1272 }
1273 }
1274 });
1275
1276 final JPopupMenu p = new JPopupMenu();
1277
1278 final Action clearFocusAction =
1279 new AbstractAction("Clear 'refine focus' field") {
1280 public void actionPerformed(ActionEvent e) {
1281 filterText.setText(null);
1282 ruleMediator.setRefinementRule(null);
1283 }
1284 };
1285
1286 final JMenuItem menuItemToggleDock = new JMenuItem("Undock/dock");
1287
1288 dockingAction =
1289 new AbstractAction("Undock") {
1290 public void actionPerformed(ActionEvent evt) {
1291 if (isDocked()) {
1292 undock();
1293 } else {
1294 dock();
1295 }
1296 }
1297 };
1298 dockingAction.putValue(
1299 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.UNDOCK));
1300 menuItemToggleDock.setAction(dockingAction);
1301
1302
1303
1304
1305 p.add(clearFocusAction);
1306 p.add(menuItemFocusOn);
1307 p.add(menuDefineAddCustomFilter);
1308 p.add(new JSeparator());
1309
1310 p.add(menuItemBestFit);
1311 p.add(new JSeparator());
1312
1313 p.add(menuItemToggleDetails);
1314 p.add(menuItemLoggerTree);
1315 p.add(menuItemToggleToolTips);
1316 p.add(new JSeparator());
1317 p.add(menuItemScrollBottom);
1318
1319 p.add(new JSeparator());
1320 p.add(menuItemToggleDock);
1321
1322 p.add(new JSeparator());
1323 p.add(menuItemColorPanel);
1324 p.add(menuItemLogPanelPreferences);
1325
1326 final PopupListener popupListener = new PopupListener(p);
1327
1328 eventsPane.addMouseListener(popupListener);
1329 table.addMouseListener(popupListener);
1330 }
1331
1332 /***
1333 * Accessor
1334 *
1335 * @return scrollToBottom
1336 *
1337 */
1338 public boolean isScrollToBottom() {
1339 return preferenceModel.isScrollToBottom();
1340 }
1341
1342 /***
1343 * Mutator
1344 *
1345 */
1346 public void toggleScrollToBottom() {
1347 preferenceModel.setScrollToBottom(!preferenceModel.isScrollToBottom());
1348 }
1349
1350 /***
1351 * Accessor
1352 *
1353 * @return namespace
1354 *
1355 * @see Profileable
1356 */
1357 public String getNamespace() {
1358 return getIdentifier();
1359 }
1360
1361 /***
1362 * Accessor
1363 *
1364 * @return identifier
1365 *
1366 * @see EventBatchListener
1367 */
1368 public String getInterestedIdentifier() {
1369 return getIdentifier();
1370 }
1371
1372 /***
1373 * Process events associated with the identifier. Currently assumes it only
1374 * receives events which share this LogPanel's identifier
1375 *
1376 * @param ident identifier shared by events
1377 * @param events list of LoggingEvent objects
1378 */
1379 public void receiveEventBatch(String ident, List events) {
1380
1381
1382
1383 if (isPaused()) {
1384 return;
1385 }
1386
1387
1388 boolean rowAdded = false;
1389
1390 int first = tableModel.getLastAdded() + 1;
1391
1392 for (Iterator iter = events.iterator(); iter.hasNext();) {
1393 LoggingEvent event = (LoggingEvent) iter.next();
1394
1395 updateOtherModels(event);
1396
1397 boolean isCurrentRowAdded = tableModel.isAddRow(event, true);
1398 rowAdded = rowAdded ? true : isCurrentRowAdded;
1399 }
1400
1401 table.getSelectionModel().setValueIsAdjusting(false);
1402
1403
1404 tableModel.notifyCountListeners();
1405
1406 if (rowAdded) {
1407 if (tableModel.isSortEnabled()) {
1408 tableModel.sort();
1409 }
1410
1411 tableModel.fireTableEvent(
1412 first, tableModel.getLastAdded(), events.size());
1413
1414 if (isScrollToBottom()) {
1415 table.scrollToBottom(
1416 table.columnAtPoint(table.getVisibleRect().getLocation()));
1417 }
1418
1419
1420 detailPaneUpdater.setSelectedRow(table.getSelectedRow());
1421 }
1422 }
1423
1424 /***
1425 * Load settings from the panel preference model
1426 *
1427 * @param event
1428 *
1429 * @see LogPanelPreferenceModel
1430 */
1431 public void loadSettings(LoadSettingsEvent event) {
1432
1433 File xmlFile = new File(SettingsManager.getInstance()
1434 .getSettingsDirectory(), URLEncoder.encode(identifier) + ".xml");
1435
1436 if (xmlFile.exists()) {
1437 XStream stream = buildXStreamForLogPanelPreference();
1438 ObjectInputStream in = null;
1439 try {
1440 FileReader r = new FileReader(xmlFile);
1441 in = stream.createObjectInputStream(r);
1442
1443 LogPanelPreferenceModel storedPrefs = (LogPanelPreferenceModel)in.readObject();
1444 preferenceModel.apply(storedPrefs);
1445 TableColumnModel columnModel = table.getColumnModel();
1446
1447 while (columnModel.getColumnCount() > 0) {
1448 columnModel.removeColumn(columnModel.getColumn(0));
1449 }
1450
1451 for (Iterator iter = preferenceModel.getVisibleColumnOrder().iterator();iter.hasNext();) {
1452 TableColumn col = (TableColumn)iter.next();
1453 columnModel.addColumn(col);
1454 }
1455
1456 try {
1457
1458 lowerPanel.setDividerLocation(in.readInt());
1459 nameTreeAndMainPanelSplit.setDividerLocation(in.readInt());
1460 detailLayout.setConversionPattern(in.readObject().toString());
1461 Point p = (Point)in.readObject();
1462 undockedFrame.setLocation(p.x, p.y);
1463 undockedFrame.setSize(((Dimension)in.readObject()));
1464
1465 int versionNumber = 0;
1466 Vector savedVector;
1467
1468
1469 try {
1470 versionNumber = in.readInt();
1471 } catch (EOFException eof){
1472 }
1473
1474
1475
1476 if (versionNumber > 0){
1477 savedVector = (Vector) in.readObject();
1478 for(int i = 0 ; i < savedVector.size() ; i++){
1479 Object item = savedVector.get(i);
1480 if(!filterExpressionVector.contains(item)){
1481 filterExpressionVector.add(item);
1482 }
1483 }
1484 }
1485
1486 } catch (EOFException eof){
1487 }
1488 } catch (Exception e) {
1489 e.printStackTrace();
1490
1491 } finally {
1492 if (in != null) {
1493 try {
1494 in.close();
1495 } catch (IOException ioe) {}
1496 }
1497 }
1498 } else {
1499 loadDefaultColumnSettings(event);
1500 }
1501
1502 logTreePanel.ignore(preferenceModel.getHiddenLoggers());
1503
1504
1505 File f2 =
1506 new File(
1507 SettingsManager.getInstance().getSettingsDirectory(), URLEncoder.encode(identifier) + COLORS_EXTENSION);
1508
1509 if (f2.exists()) {
1510 loadColorSettings(f2);
1511 } else {
1512 f2 =
1513 new File(
1514 SettingsManager.getInstance().getSettingsDirectory(), identifier + COLORS_EXTENSION);
1515 }
1516 }
1517
1518 /***
1519 * Save preferences to the panel preference model
1520 *
1521 * @param event
1522 *
1523 * @see LogPanelPreferenceModel
1524 */
1525 public void saveSettings(SaveSettingsEvent event) {
1526 File xmlFile = new File(SettingsManager.getInstance()
1527 .getSettingsDirectory(), URLEncoder.encode(identifier) + ".xml");
1528
1529 preferenceModel.setHiddenLoggers(new HashSet(logTreePanel.getHiddenSet()));
1530 List visibleOrder = new ArrayList();
1531 Enumeration cols = table.getColumnModel().getColumns();
1532 while (cols.hasMoreElements()) {
1533 TableColumn c = (TableColumn)cols.nextElement();
1534 visibleOrder.add(c);
1535 }
1536 preferenceModel.setVisibleColumnOrder(visibleOrder);
1537
1538 XStream stream = buildXStreamForLogPanelPreference();
1539 ObjectOutputStream s = null;
1540 try {
1541 FileWriter w = new FileWriter(xmlFile);
1542 s = stream.createObjectOutputStream(w);
1543 s.writeObject(preferenceModel);
1544 s.writeInt(lowerPanel.getDividerLocation());
1545 s.writeInt(nameTreeAndMainPanelSplit.getDividerLocation());
1546 s.writeObject(detailLayout.getConversionPattern());
1547 s.writeObject(undockedFrame.getLocation());
1548 s.writeObject(undockedFrame.getSize());
1549
1550 s.writeInt(LOG_PANEL_SERIALIZATION_VERSION_NUMBER);
1551 s.writeObject(filterExpressionVector);
1552 } catch (Exception ex) {
1553 ex.printStackTrace();
1554
1555 } finally {
1556 if (s != null) {
1557 try {
1558 s.close();
1559 } catch (IOException ioe) {}
1560 }
1561 }
1562
1563
1564 saveColorSettings();
1565 }
1566
1567 private XStream buildXStreamForLogPanelPreference() {
1568 XStream stream = new XStream(new DomDriver());
1569 stream.registerConverter(new TableColumnConverter());
1570 return stream;
1571 }
1572
1573 /***
1574 * Display the panel preferences frame
1575 */
1576 void showPreferences() {
1577 preferencesFrame.setVisible(true);
1578 }
1579
1580 /***
1581 * Display the color rule frame
1582 */
1583 void showColorPreferences() {
1584 colorFrame.pack();
1585 colorFrame.setVisible(true);
1586 }
1587
1588 /***
1589 * Toggle panel preference for detail visibility on or off
1590 */
1591 void toggleDetailVisible() {
1592 preferenceModel.setDetailPaneVisible(
1593 !preferenceModel.isDetailPaneVisible());
1594 }
1595
1596 /***
1597 * Accessor
1598 *
1599 * @return detail visibility flag
1600 */
1601 boolean isDetailVisible() {
1602 return preferenceModel.isDetailPaneVisible();
1603 }
1604
1605 /***
1606 * Toggle panel preference for logger tree visibility on or off
1607 */
1608 void toggleLogTreeVisible() {
1609 preferenceModel.setLogTreePanelVisible(
1610 !preferenceModel.isLogTreePanelVisible());
1611 }
1612
1613 /***
1614 * Accessor
1615 *
1616 * @return logger tree visibility flag
1617 */
1618 boolean isLogTreeVisible() {
1619 return preferenceModel.isLogTreePanelVisible();
1620 }
1621
1622 /***
1623 * Return all events
1624 *
1625 * @return list of LoggingEvents
1626 */
1627 List getEvents() {
1628 return tableModel.getAllEvents();
1629 }
1630
1631 /***
1632 * Return the events that are visible with the current filter applied
1633 *
1634 * @return list of LoggingEvents
1635 */
1636 List getFilteredEvents() {
1637 return tableModel.getFilteredEvents();
1638 }
1639
1640 List getMatchingEvents(Rule rule) {
1641 return tableModel.getMatchingEvents(rule);
1642 }
1643
1644 /***
1645 * Remove all events
1646 */
1647 void clearEvents() {
1648 clearModel();
1649 }
1650
1651 /***
1652 * Accessor
1653 *
1654 * @return identifier
1655 */
1656 String getIdentifier() {
1657 return identifier;
1658 }
1659
1660 /***
1661 * Undocks this DockablePanel by removing the panel from the LogUI window
1662 * and placing it inside it's own JFrame.
1663 */
1664 void undock() {
1665 int row = table.getSelectedRow();
1666 setDocked(false);
1667 externalPanel.removeAll();
1668 findPanel.removeAll();
1669 findPanel.add(findField);
1670
1671 externalPanel.add(undockedToolbar, BorderLayout.NORTH);
1672 externalPanel.add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
1673 externalPanel.setDocked(false);
1674
1675 undockedFrame.setVisible(true);
1676 dockingAction.putValue(Action.NAME, "Dock");
1677 dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_DOCK);
1678 if (row > -1) {
1679 table.scrollToRow(row, table.columnAtPoint(table.getVisibleRect().getLocation()));
1680 }
1681 }
1682
1683 /***
1684 * Add an eventCountListener
1685 *
1686 * @param l
1687 */
1688 void addEventCountListener(EventCountListener l) {
1689 tableModel.addEventCountListener(l);
1690 }
1691
1692 /***
1693 * Accessor
1694 *
1695 * @return paused flag
1696 */
1697 boolean isPaused() {
1698 return paused;
1699 }
1700
1701 /***
1702 * Modifies the Paused property and notifies the listeners
1703 *
1704 * @param paused
1705 */
1706 void setPaused(boolean paused) {
1707 boolean oldValue = this.paused;
1708 this.paused = paused;
1709 firePropertyChange("paused", oldValue, paused);
1710 }
1711
1712 /***
1713 * Change the selected event on the log panel
1714 *
1715 * @param eventNumber
1716 */
1717 void setSelectedEvent(int eventNumber){
1718 table.scrollToRow(eventNumber - 1, 0);
1719 }
1720
1721 /***
1722 * Add a preference propertyChangeListener
1723 *
1724 * @param listener
1725 */
1726 void addPreferencePropertyChangeListener(PropertyChangeListener listener) {
1727 preferenceModel.addPropertyChangeListener(listener);
1728 }
1729
1730 /***
1731 * Toggle the LoggingEvent container from either managing a cyclic buffer of
1732 * events or an ArrayList of events
1733 */
1734 void toggleCyclic() {
1735 tableModel.setCyclic(!tableModel.isCyclic());
1736 }
1737
1738 /***
1739 * Accessor
1740 *
1741 * @return flag answering if LoggingEvent container is a cyclic buffer
1742 */
1743 boolean isCyclic() {
1744 return tableModel.isCyclic();
1745 }
1746
1747 public boolean updateRule(String ruleText) {
1748 if ((ruleText == null) || (ruleText.equals(""))) {
1749 findRule = null;
1750 colorizer.setFindRule(null);
1751 findField.setToolTipText(
1752 "Enter expression - right click or ctrl-space for menu");
1753 return false;
1754 } else {
1755
1756 preferenceModel.setScrollToBottom(false);
1757 try {
1758 findField.setToolTipText(
1759 "Enter expression - right click or ctrl-space for menu");
1760 findRule = ExpressionRule.getRule(ruleText);
1761 colorizer.setFindRule(findRule);
1762
1763 return true;
1764 } catch (IllegalArgumentException re) {
1765 findField.setToolTipText(re.getMessage());
1766 colorizer.setFindRule(null);
1767
1768 return false;
1769 }
1770 }
1771 }
1772
1773 /***
1774 * Display the detail pane, using the last known divider location
1775 */
1776 private void showDetailPane() {
1777 lowerPanel.setDividerSize(dividerSize);
1778 lowerPanel.setDividerLocation(lastDetailPanelSplitLocation);
1779 detailPanel.setVisible(true);
1780 lowerPanel.repaint();
1781 }
1782
1783 /***
1784 * Hide the detail pane, holding the current divider location for later use
1785 */
1786 private void hideDetailPane() {
1787 int currentSize = lowerPanel.getHeight() - lowerPanel.getDividerSize();
1788
1789 if (currentSize > 0) {
1790 lastDetailPanelSplitLocation =
1791 (double) lowerPanel.getDividerLocation() / currentSize;
1792 }
1793
1794 lowerPanel.setDividerSize(0);
1795 detailPanel.setVisible(false);
1796 lowerPanel.repaint();
1797 }
1798
1799 /***
1800 * Display the log tree pane, using the last known divider location
1801 */
1802 private void showLogTreePanel() {
1803 nameTreeAndMainPanelSplit.setDividerSize(dividerSize);
1804 nameTreeAndMainPanelSplit.setDividerLocation(
1805 lastLogTreePanelSplitLocation);
1806 logTreePanel.setVisible(true);
1807 nameTreeAndMainPanelSplit.repaint();
1808 }
1809
1810 /***
1811 * Hide the log tree pane, holding the current divider location for later use
1812 */
1813 private void hideLogTreePanel() {
1814
1815 int currentSize = nameTreeAndMainPanelSplit.getWidth() - nameTreeAndMainPanelSplit.getDividerSize() - 1;
1816
1817 if (currentSize > 0) {
1818 lastLogTreePanelSplitLocation =
1819 (double) nameTreeAndMainPanelSplit.getDividerLocation() / currentSize;
1820 }
1821 nameTreeAndMainPanelSplit.setDividerSize(0);
1822 logTreePanel.setVisible(false);
1823 nameTreeAndMainPanelSplit.repaint();
1824 }
1825
1826 /***
1827 * Return a toolbar used by the undocked LogPanel's frame
1828 *
1829 * @return toolbar
1830 */
1831 private JToolBar createDockwindowToolbar() {
1832 final JToolBar toolbar = new JToolBar();
1833 toolbar.setFloatable(false);
1834
1835 final Action dockPauseAction =
1836 new AbstractAction("Pause") {
1837 public void actionPerformed(ActionEvent evt) {
1838 setPaused(!isPaused());
1839 }
1840 };
1841
1842 dockPauseAction.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_P));
1843 dockPauseAction.putValue(
1844 Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("F12"));
1845 dockPauseAction.putValue(
1846 Action.SHORT_DESCRIPTION,
1847 "Halts the display, while still allowing events to stream in the background");
1848 dockPauseAction.putValue(
1849 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.PAUSE));
1850
1851 final SmallToggleButton dockPauseButton =
1852 new SmallToggleButton(dockPauseAction);
1853 dockPauseButton.setText("");
1854
1855 dockPauseButton.getModel().setSelected(isPaused());
1856
1857 addPropertyChangeListener(
1858 "paused",
1859 new PropertyChangeListener() {
1860 public void propertyChange(PropertyChangeEvent evt) {
1861 dockPauseButton.getModel().setSelected(isPaused());
1862 }
1863 });
1864 toolbar.add(dockPauseButton);
1865
1866 Action dockShowPrefsAction =
1867 new AbstractAction("") {
1868 public void actionPerformed(ActionEvent arg0) {
1869 showPreferences();
1870 }
1871 };
1872
1873 dockShowPrefsAction.putValue(
1874 Action.SHORT_DESCRIPTION, "Define preferences...");
1875 dockShowPrefsAction.putValue(
1876 Action.SMALL_ICON, ChainsawIcons.ICON_PREFERENCES);
1877
1878 toolbar.add(new SmallButton(dockShowPrefsAction));
1879
1880 Action dockToggleLogTreeAction =
1881 new AbstractAction() {
1882 public void actionPerformed(ActionEvent e) {
1883 toggleLogTreeVisible();
1884 }
1885 };
1886
1887 dockToggleLogTreeAction.putValue(Action.SHORT_DESCRIPTION, "Toggles the Logger Tree Pane");
1888 dockToggleLogTreeAction.putValue("enabled", Boolean.TRUE);
1889 dockToggleLogTreeAction.putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_T));
1890 dockToggleLogTreeAction.putValue(
1891 Action.ACCELERATOR_KEY,
1892 KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.ALT_MASK));
1893 dockToggleLogTreeAction.putValue(
1894 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.WINDOW_ICON));
1895
1896 final SmallToggleButton toggleLogTreeButton =
1897 new SmallToggleButton(dockToggleLogTreeAction);
1898 preferenceModel.addPropertyChangeListener("logTreePanelVisible", new PropertyChangeListener() {
1899 public void propertyChange(PropertyChangeEvent evt) {
1900 toggleLogTreeButton.setSelected(preferenceModel.isLogTreePanelVisible());
1901 }
1902 });
1903
1904 toggleLogTreeButton.setSelected(isLogTreeVisible());
1905 toolbar.add(toggleLogTreeButton);
1906 toolbar.addSeparator();
1907
1908 final Action undockedClearAction =
1909 new AbstractAction("Clear") {
1910 public void actionPerformed(ActionEvent arg0) {
1911 clearModel();
1912 }
1913 };
1914
1915 undockedClearAction.putValue(
1916 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.DELETE));
1917 undockedClearAction.putValue(
1918 Action.SHORT_DESCRIPTION, "Removes all the events from the current view");
1919
1920 final SmallButton dockClearButton = new SmallButton(undockedClearAction);
1921 dockClearButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
1922 KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, InputEvent.CTRL_MASK),
1923 undockedClearAction.getValue(Action.NAME));
1924 dockClearButton.getActionMap().put(
1925 undockedClearAction.getValue(Action.NAME), undockedClearAction);
1926
1927 dockClearButton.setText("");
1928 toolbar.add(dockClearButton);
1929 toolbar.addSeparator();
1930
1931 Action dockToggleScrollToBottomAction =
1932 new AbstractAction("Toggles Scroll to Bottom") {
1933 public void actionPerformed(ActionEvent e) {
1934 toggleScrollToBottom();
1935 }
1936 };
1937
1938 dockToggleScrollToBottomAction.putValue(Action.SHORT_DESCRIPTION, "Toggles Scroll to Bottom");
1939 dockToggleScrollToBottomAction.putValue("enabled", Boolean.TRUE);
1940 dockToggleScrollToBottomAction.putValue(
1941 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.SCROLL_TO_BOTTOM));
1942
1943 final SmallToggleButton toggleScrollToBottomButton =
1944 new SmallToggleButton(dockToggleScrollToBottomAction);
1945 preferenceModel.addPropertyChangeListener("scrollToBottom", new PropertyChangeListener() {
1946 public void propertyChange(PropertyChangeEvent evt) {
1947 toggleScrollToBottomButton.setSelected(isScrollToBottom());
1948 }
1949 });
1950
1951 toggleScrollToBottomButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
1952 KeyStroke.getKeyStroke(KeyEvent.VK_B, InputEvent.CTRL_MASK),
1953 dockToggleScrollToBottomAction.getValue(Action.NAME));
1954 toggleScrollToBottomButton.getActionMap().put(
1955 dockToggleScrollToBottomAction.getValue(Action.NAME), dockToggleScrollToBottomAction);
1956
1957 toggleScrollToBottomButton.setSelected(isScrollToBottom());
1958 toggleScrollToBottomButton.setText("");
1959 toolbar.add(toggleScrollToBottomButton);
1960 toolbar.addSeparator();
1961
1962 findField = new JTextField();
1963 findField.addKeyListener(
1964 new ExpressionRuleContext(filterModel, findField));
1965
1966 final Action undockedFindNextAction =
1967 new AbstractAction() {
1968 public void actionPerformed(ActionEvent e) {
1969 findNext();
1970 }
1971 };
1972
1973 undockedFindNextAction.putValue(Action.NAME, "Find next");
1974 undockedFindNextAction.putValue(
1975 Action.SHORT_DESCRIPTION,
1976 "Find the next occurrence of the rule from the current row");
1977 undockedFindNextAction.putValue(
1978 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.DOWN));
1979
1980 SmallButton undockedFindNextButton =
1981 new SmallButton(undockedFindNextAction);
1982
1983 undockedFindNextButton.setAction(undockedFindNextAction);
1984 undockedFindNextButton.setText("");
1985 undockedFindNextButton.getActionMap().put(
1986 undockedFindNextAction.getValue(Action.NAME), undockedFindNextAction);
1987 undockedFindNextButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
1988 KeyStroke.getKeyStroke("F3"),
1989 undockedFindNextAction.getValue(Action.NAME));
1990
1991 final Action undockedFindPreviousAction =
1992 new AbstractAction() {
1993 public void actionPerformed(ActionEvent e) {
1994 findPrevious();
1995 }
1996 };
1997
1998 undockedFindPreviousAction.putValue(Action.NAME, "Find previous");
1999 undockedFindPreviousAction.putValue(
2000 Action.SHORT_DESCRIPTION,
2001 "Find the previous occurrence of the rule from the current row");
2002 undockedFindPreviousAction.putValue(
2003 Action.SMALL_ICON, new ImageIcon(ChainsawIcons.UP));
2004
2005 SmallButton undockedFindPreviousButton =
2006 new SmallButton(undockedFindPreviousAction);
2007
2008 undockedFindPreviousButton.setAction(undockedFindPreviousAction);
2009 undockedFindPreviousButton.setText("");
2010 undockedFindPreviousButton.getActionMap().put(
2011 undockedFindPreviousAction.getValue(Action.NAME),
2012 undockedFindPreviousAction);
2013 undockedFindPreviousButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2014 .put(
2015 KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_MASK),
2016 undockedFindPreviousAction.getValue(Action.NAME));
2017
2018 Dimension findSize = new Dimension(170, 22);
2019 Dimension findPanelSize = new Dimension(175, 30);
2020 findPanel.setPreferredSize(findPanelSize);
2021 findPanel.setMaximumSize(findPanelSize);
2022 findPanel.setMinimumSize(findPanelSize);
2023 findField.setPreferredSize(findSize);
2024 findField.setMaximumSize(findSize);
2025 findField.setMinimumSize(findSize);
2026 findPanel.setAlignmentY(Component.CENTER_ALIGNMENT);
2027 findField.setAlignmentY(Component.CENTER_ALIGNMENT);
2028
2029 toolbar.add(findPanel);
2030 toolbar.add(undockedFindNextButton);
2031 toolbar.add(undockedFindPreviousButton);
2032
2033 toolbar.addSeparator();
2034
2035 Action redockAction =
2036 new AbstractAction("", ChainsawIcons.ICON_DOCK) {
2037 public void actionPerformed(ActionEvent arg0) {
2038 dock();
2039 }
2040 };
2041
2042 redockAction.putValue(
2043 Action.SHORT_DESCRIPTION,
2044 "Docks this window back with the main Chainsaw window");
2045
2046 SmallButton redockButton = new SmallButton(redockAction);
2047 toolbar.add(redockButton);
2048
2049 return toolbar;
2050 }
2051
2052 /***
2053 * Update the status bar with current selected row and row count
2054 */
2055 private void updateStatusBar() {
2056 SwingUtilities.invokeLater(
2057 new Runnable() {
2058 public void run() {
2059 statusBar.setSelectedLine(
2060 table.getSelectedRow() + 1, tableModel.getRowCount(),
2061 tableModel.size());
2062 }
2063 });
2064 }
2065
2066 /***
2067 * Update the detail pane layout text
2068 *
2069 * @param conversionPattern layout text
2070 */
2071 private void setDetailPaneConversionPattern(String conversionPattern) {
2072 String oldPattern = getDetailPaneConversionPattern();
2073 ((EventDetailLayout) detailLayout).setConversionPattern(conversionPattern);
2074 firePropertyChange(
2075 "detailPaneConversionPattern", oldPattern,
2076 getDetailPaneConversionPattern());
2077 }
2078
2079 /***
2080 * Accessor
2081 *
2082 * @return conversionPattern layout text
2083 */
2084 private String getDetailPaneConversionPattern() {
2085 return ((EventDetailLayout) detailLayout).getConversionPattern();
2086 }
2087
2088 /***
2089 * Reset the LoggingEvent container, detail panel and status bar
2090 */
2091 private void clearModel() {
2092 tableModel.clearModel();
2093
2094 synchronized (detail) {
2095 detailPaneUpdater.setSelectedRow(-1);
2096 detail.notify();
2097 }
2098
2099 statusBar.setNothingSelected();
2100 }
2101
2102 /***
2103 * Finds the next row matching the current find rule, and ensures it is made
2104 * visible
2105 *
2106 */
2107 public void findNext() {
2108 updateRule(findField.getText());
2109
2110 if (findRule != null) {
2111 try {
2112 final int nextRow =
2113 tableModel.find(findRule, table.getSelectedRow() + 1, true);
2114
2115 if (nextRow > -1) {
2116 table.scrollToRow(
2117 nextRow, table.columnAtPoint(table.getVisibleRect().getLocation()));
2118 findField.setToolTipText("Enter an expression");
2119 }
2120 } catch (IllegalArgumentException iae) {
2121 findField.setToolTipText(iae.getMessage());
2122 colorizer.setFindRule(null);
2123 }
2124 }
2125 }
2126
2127 /***
2128 * Finds the previous row matching the current find rule, and ensures it is made
2129 * visible
2130 *
2131 */
2132 public void findPrevious() {
2133 updateRule(findField.getText());
2134
2135 if (findRule != null) {
2136 try {
2137 final int previousRow =
2138 tableModel.find(findRule, table.getSelectedRow() - 1, false);
2139
2140 if (previousRow > -1) {
2141 table.scrollToRow(
2142 previousRow,
2143 table.columnAtPoint(table.getVisibleRect().getLocation()));
2144 findField.setToolTipText("Enter an expression");
2145 }
2146 } catch (IllegalArgumentException iae) {
2147 findField.setToolTipText(iae.getMessage());
2148 }
2149 }
2150 }
2151
2152 /***
2153 * Docks this DockablePanel by hiding the JFrame and placing the Panel back
2154 * inside the LogUI window.
2155 */
2156 private void dock() {
2157
2158 int row = table.getSelectedRow();
2159 setDocked(true);
2160 undockedFrame.setVisible(false);
2161 removeAll();
2162
2163 add(nameTreeAndMainPanelSplit, BorderLayout.CENTER);
2164 externalPanel.setDocked(true);
2165 dockingAction.putValue(Action.NAME, "Undock");
2166 dockingAction.putValue(Action.SMALL_ICON, ChainsawIcons.ICON_UNDOCK);
2167 if (row > -1) {
2168 table.scrollToRow(row, table.columnAtPoint(table.getVisibleRect().getLocation()));
2169 }
2170 }
2171
2172 /***
2173 * Save panel color settings
2174 */
2175 private void saveColorSettings() {
2176 ObjectOutputStream o = null;
2177
2178 try {
2179 File f = new File(SettingsManager.getInstance().getSettingsDirectory(),
2180 URLEncoder.encode(getIdentifier() + COLORS_EXTENSION));
2181 logger.debug("writing colors to file: " + f);
2182
2183 o = new ObjectOutputStream(
2184 new BufferedOutputStream(new FileOutputStream(f)));
2185
2186 o.writeObject(colorizer.getRules());
2187 o.flush();
2188 } catch (FileNotFoundException fnfe) {
2189 fnfe.printStackTrace();
2190 } catch (IOException ioe) {
2191 ioe.printStackTrace();
2192 } finally {
2193 try {
2194 if (o != null) {
2195 o.close();
2196 }
2197 } catch (IOException ioe) {
2198 ioe.printStackTrace();
2199 }
2200 }
2201 }
2202
2203 /***
2204 * Load default column settings if no settings exist for this identifier
2205 *
2206 * @param event
2207 */
2208 private void loadDefaultColumnSettings(LoadSettingsEvent event) {
2209 String columnOrder = event.getSetting(TABLE_COLUMN_ORDER);
2210
2211 TableColumnModel columnModel = table.getColumnModel();
2212
2213 Map columnNameMap = new HashMap();
2214
2215 for (int i = 0; i < columnModel.getColumnCount(); i++) {
2216 columnNameMap.put(table.getColumnName(i), columnModel.getColumn(i));
2217 }
2218
2219 int index = 0;
2220 StringTokenizer tok = new StringTokenizer(columnOrder, ",");
2221 List sortedColumnList = new ArrayList();
2222
2223
2224
2225
2226
2227
2228 while (tok.hasMoreElements()) {
2229 String element = (String) tok.nextElement();
2230 TableColumn column = (TableColumn) columnNameMap.get(element);
2231
2232 if (column != null) {
2233 sortedColumnList.add(column);
2234 table.removeColumn(column);
2235 }
2236 }
2237 preferenceModel.setDetailPaneVisible(event.asBoolean("detailPaneVisible"));
2238 preferenceModel.setLogTreePanelVisible(event.asBoolean("logTreePanelVisible"));
2239
2240 for (Iterator iter = sortedColumnList.iterator(); iter.hasNext();) {
2241 TableColumn element = (TableColumn) iter.next();
2242 if (preferenceModel.addColumn(element)) {
2243 table.addColumn(element);
2244 preferenceModel.setColumnVisible(element.getHeaderValue().toString(), true);
2245 }
2246 }
2247
2248 String columnWidths = event.getSetting(TABLE_COLUMN_WIDTHS);
2249
2250 tok = new StringTokenizer(columnWidths, ",");
2251 index = 0;
2252
2253 while (tok.hasMoreElements()) {
2254 String element = (String) tok.nextElement();
2255
2256 try {
2257 int width = Integer.parseInt(element);
2258
2259 if (index > (columnModel.getColumnCount() - 1)) {
2260 logger.warn(
2261 "loadsettings - failed attempt to set width for index " + index
2262 + ", width " + element);
2263 } else {
2264 columnModel.getColumn(index).setPreferredWidth(width);
2265 }
2266
2267 index++;
2268 } catch (NumberFormatException e) {
2269 logger.error("Error decoding a Table width", e);
2270 }
2271 }
2272 undockedFrame.setSize(getSize());
2273 undockedFrame.setLocation(getBounds().x, getBounds().y);
2274
2275 repaint();
2276 }
2277
2278 public JTextField getFindTextField() {
2279 return findField;
2280 }
2281
2282 /***
2283 * Load panel color settings
2284 */
2285 private void loadColorSettings(File f) {
2286 if (f.exists()) {
2287 ObjectInputStream s = null;
2288
2289 try {
2290 s = new ObjectInputStream(
2291 new BufferedInputStream(new FileInputStream(f)));
2292
2293 Map map = (Map) s.readObject();
2294 colorizer.setRules(map);
2295 } catch (EOFException eof) {
2296 }catch (IOException ioe) {
2297 ioe.printStackTrace();
2298
2299 f.delete();
2300 } catch (ClassNotFoundException cnfe) {
2301 cnfe.printStackTrace();
2302 } finally {
2303 if (s != null) {
2304 try {
2305 s.close();
2306 } catch (IOException ioe) {
2307 ioe.printStackTrace();
2308 }
2309 }
2310 }
2311 }
2312 }
2313
2314 /***
2315 * Iterate over all values in the column and return the longest width
2316 *
2317 * @param index column index
2318 *
2319 * @return longest width - relies on FontMetrics.stringWidth for calculation
2320 */
2321 private int getMaxColumnWidth(int index) {
2322 FontMetrics metrics = getGraphics().getFontMetrics();
2323 int longestWidth =
2324 metrics.stringWidth(" " + table.getColumnName(index) + " ")
2325 + (2 * table.getColumnModel().getColumnMargin());
2326
2327 for (int i = 0, j = tableModel.getRowCount(); i < j; i++) {
2328 Component c =
2329 renderer.getTableCellRendererComponent(
2330 table, table.getValueAt(i, index), false, false, i, index);
2331
2332 if (c instanceof JLabel) {
2333 longestWidth =
2334 Math.max(longestWidth, metrics.stringWidth(((JLabel) c).getText()));
2335 }
2336 }
2337
2338 return longestWidth + 5;
2339 }
2340
2341 /***
2342 * ensures the Entry map of all the unque logger names etc, that is used for
2343 * the Filter panel is updated with any new information from the event
2344 *
2345 * @param event
2346 */
2347 private void updateOtherModels(LoggingEvent event) {
2348
2349
2350
2351
2352 tableModel.addLoggerName(event.getLoggerName());
2353
2354 filterModel.processNewLoggingEvent(event);
2355 }
2356
2357 /***
2358 * This class receives notification when the Refine focus text field is
2359 * updated, where a backgrounh thread periodically wakes up and checks if
2360 * they have stopped typing yet. This ensures that the filtering of the
2361 * model is not done for every single character typed.
2362 *
2363 * @author Paul Smith psmith
2364 */
2365 private final class DelayedFilterTextDocumentListener
2366 implements DocumentListener {
2367 private static final long CHECK_PERIOD = 1000;
2368 private final JTextField filterText;
2369 private long lastTimeStamp = System.currentTimeMillis();
2370 private final Thread delayThread;
2371 private final String defaultToolTip;
2372 private String lastFilterText = null;
2373
2374 private DelayedFilterTextDocumentListener(final JTextField filterText) {
2375 super();
2376 this.filterText = filterText;
2377 this.defaultToolTip = filterText.getToolTipText();
2378
2379 this.delayThread =
2380 new Thread(
2381 new Runnable() {
2382 public void run() {
2383 while (true) {
2384 try {
2385 Thread.sleep(CHECK_PERIOD);
2386 } catch (InterruptedException e) {
2387 }
2388
2389 if (
2390 (System.currentTimeMillis() - lastTimeStamp) < CHECK_PERIOD) {
2391
2392
2393
2394 } else if (
2395 (System.currentTimeMillis() - lastTimeStamp) < (2 * CHECK_PERIOD)) {
2396
2397
2398
2399 if (filterText != null && (!(filterText.getText().equals(lastFilterText)))) {
2400 lastFilterText = filterText.getText();
2401 setFilter();
2402 }
2403 } else {
2404
2405
2406
2407 }
2408 }
2409 }
2410 });
2411
2412 delayThread.setPriority(Thread.MIN_PRIORITY);
2413 delayThread.start();
2414 }
2415
2416 /***
2417 * Update timestamp
2418 *
2419 * @param e
2420 */
2421 public void insertUpdate(DocumentEvent e) {
2422 notifyChange();
2423 }
2424
2425 /***
2426 * Update timestamp
2427 *
2428 * @param e
2429 */
2430 public void removeUpdate(DocumentEvent e) {
2431 notifyChange();
2432 }
2433
2434 /***
2435 * Update timestamp
2436 *
2437 * @param e
2438 */
2439 public void changedUpdate(DocumentEvent e) {
2440 notifyChange();
2441 }
2442
2443 /***
2444 * Update timestamp
2445 */
2446 private void notifyChange() {
2447 this.lastTimeStamp = System.currentTimeMillis();
2448 }
2449
2450 /***
2451 * Update refinement rule based on the entered expression.
2452 */
2453 private void setFilter() {
2454 if (filterText.getText().equals("")) {
2455 ruleMediator.setRefinementRule(null);
2456 filterText.setToolTipText(defaultToolTip);
2457 } else {
2458 try {
2459 ruleMediator.setRefinementRule(
2460 ExpressionRule.getRule(filterText.getText()));
2461 filterText.setToolTipText(defaultToolTip);
2462 } catch (IllegalArgumentException iae) {
2463 filterText.setToolTipText(iae.getMessage());
2464 }
2465 }
2466 }
2467 }
2468
2469 /***
2470 * Update active tooltip
2471 */
2472 private final class TableColumnDetailMouseListener extends MouseMotionAdapter {
2473 private int currentRow = -1;
2474
2475 private TableColumnDetailMouseListener() {
2476 }
2477
2478 /***
2479 * Update tooltip based on mouse position
2480 *
2481 * @param evt
2482 */
2483 public void mouseMoved(MouseEvent evt) {
2484 currentPoint = evt.getPoint();
2485
2486 if (preferenceModel.isToolTips()) {
2487 int row = table.rowAtPoint(evt.getPoint());
2488
2489 if ((row == currentRow) || (row == -1)) {
2490 return;
2491 }
2492
2493 currentRow = row;
2494
2495 LoggingEvent event = tableModel.getRow(currentRow);
2496
2497 if (event != null) {
2498 StringBuffer buf = new StringBuffer();
2499 buf.append(detailLayout.getHeader())
2500 .append(detailLayout.format(event)).append(
2501 detailLayout.getFooter());
2502 table.setToolTipText(buf.toString());
2503 }
2504 } else {
2505 table.setToolTipText(null);
2506 }
2507 }
2508 }
2509
2510
2511
2512 private class ChainsawTableColumnModelListener
2513 implements TableColumnModelListener {
2514 private ChainsawTableColumnModelListener() {
2515 }
2516
2517 /***
2518 * If a new column was added to the display and that column was the exception column,
2519 * set the cell editor to the throwablerenderer
2520 *
2521 * @param e
2522 */
2523 public void columnAdded(TableColumnModelEvent e) {
2524 Enumeration enumeration = table.getColumnModel().getColumns();
2525
2526 while (enumeration.hasMoreElements()) {
2527 TableColumn column = (TableColumn) enumeration.nextElement();
2528
2529 if (
2530 (column.getModelIndex() + 1) == ChainsawColumns.INDEX_THROWABLE_COL_NAME) {
2531 column.setCellEditor(throwableRenderPanel);
2532 }
2533 }
2534 }
2535
2536 /***
2537 * Update sorted column
2538 *
2539 * @param e
2540 */
2541 public void columnRemoved(TableColumnModelEvent e) {
2542 table.updateSortedColumn();
2543 }
2544
2545 /***
2546 * Update sorted column
2547 *
2548 * @param e
2549 */
2550 public void columnMoved(TableColumnModelEvent e) {
2551 table.updateSortedColumn();
2552 }
2553
2554 /***
2555 * Ignore margin changed
2556 *
2557 * @param e
2558 */
2559 public void columnMarginChanged(ChangeEvent e) {
2560 }
2561
2562 /***
2563 * Ignore selection changed
2564 *
2565 * @param e
2566 */
2567 public void columnSelectionChanged(ListSelectionEvent e) {
2568 }
2569 }
2570
2571 /***
2572 * Thread that periodically checks if the selected row has changed, and if
2573 * it was, updates the Detail Panel with the detailed Logging information
2574 */
2575 private class DetailPaneUpdater implements PropertyChangeListener {
2576 private int selectedRow = -1;
2577
2578 private DetailPaneUpdater() {
2579 }
2580
2581 /***
2582 * Update detail pane to display information about the LoggingEvent at index row
2583 *
2584 * @param row
2585 */
2586 private void setSelectedRow(int row) {
2587 selectedRow = row;
2588 updateDetailPane();
2589 }
2590
2591 /***
2592 * Update detail pane
2593 */
2594 private void updateDetailPane() {
2595
2596
2597
2598 if (!detail.isVisible()) {
2599 return;
2600 }
2601
2602 LoggingEvent event = null;
2603 if (selectedRow != -1) {
2604 event = tableModel.getRow(selectedRow);
2605
2606 if (event != null) {
2607 final StringBuffer buf = new StringBuffer();
2608 buf.append(detailLayout.getHeader())
2609 .append(detailLayout.format(event)).append(
2610 detailLayout.getFooter());
2611 if (buf.length() > 0) {
2612 try {
2613 final Document doc = detail.getEditorKit().createDefaultDocument();
2614 detail.getEditorKit().read(new StringReader(buf.toString()), doc, 0);
2615 SwingUtilities.invokeLater(new Runnable() {
2616 public void run() {
2617 detail.setDocument(doc);
2618 detail.setCaretPosition(0);
2619 }
2620 });
2621 } catch (Exception e) {}
2622 }
2623 }
2624 }
2625
2626 if (event == null) {
2627 try {
2628 final Document doc = detail.getEditorKit().createDefaultDocument();
2629 detail.getEditorKit().read(new StringReader("<html>Nothing selected</html>"), doc, 0);
2630 SwingUtilities.invokeLater(new Runnable() {
2631 public void run() {
2632 detail.setDocument(doc);
2633 detail.setCaretPosition(0);
2634 }
2635 });
2636 } catch (Exception e) {}
2637 }
2638 }
2639
2640 /***
2641 * Update detail pane layout if it's changed
2642 *
2643 * @param arg0
2644 */
2645 public void propertyChange(PropertyChangeEvent arg0) {
2646 SwingUtilities.invokeLater(
2647 new Runnable() {
2648 public void run() {
2649 updateDetailPane();
2650 }
2651 });
2652 }
2653 }
2654 }