/**
* Copyright 2012-2015 Rafal Lewczuk <rafal.lewczuk@jitlogic.com>
* <p/>
* This is free software. You can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
* <p/>
* This software is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* <p/>
* You should have received a copy of the GNU General Public License
* along with this software. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jitlogic.zorka.viewer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
public class TraceDetailPanel extends JPanel {
private TraceDataSet dataSet;
private ErrorDetailView pnlStackTrace;
/** This table lists method call tree of a selected trace. */
private JTable tblTraceDetail;
/** Table model for tblTraceDetail */
private TraceDetailTableModel tbmTraceDetail;
private JTextField txtSearch;
private double minPct;
private boolean errOnly;
private Set<String> omits = new HashSet<String>();
private JButton btnErrorFilterErrors;
private class PctFilterAction extends AbstractAction {
private double pct;
public PctFilterAction(String icon, double pct) {
super("", ResourceManager.getIcon16x16("filter-pct-"+icon));
this.pct = pct;
}
@Override
public void actionPerformed(ActionEvent e) {
minPct = pct;
tbmTraceDetail.filterOut(minPct, errOnly, omits);
}
}
private class ErrFilterAction extends AbstractAction {
public ErrFilterAction(String title, String icon) {
super(title, ResourceManager.getIcon16x16(icon));
}
@Override
public void actionPerformed(ActionEvent e) {
errOnly = !errOnly;
btnErrorFilterErrors.setSelected(errOnly);
tbmTraceDetail.filterOut(minPct, errOnly, omits);
}
}
private class SearchAction extends AbstractAction {
public SearchAction(String title, String icon, boolean forward) {
super(title, ResourceManager.getIcon16x16(icon));
}
@Override
public void actionPerformed(ActionEvent e) {
Pattern pattern = Pattern.compile(txtSearch.getText().trim().length() > 0 ? ".*"+txtSearch.getText().trim()+".*" : ".*");
for (int i = tblTraceDetail.getSelectedRow() + 1; i < tbmTraceDetail.getRowCount(); i++) {
ViewerTraceRecord rec = tbmTraceDetail.getRecord(i);
if (pattern.matcher(rec.getClassName()).matches() || pattern.matcher(rec.getMethodName()).matches()) {
tblTraceDetail.getSelectionModel().setSelectionInterval(i, i);
return;
}
}
}
}
private class ClearFiltersAction extends AbstractAction {
public ClearFiltersAction() {
super("", ResourceManager.getIcon16x16("clear"));
}
@Override
public void actionPerformed(ActionEvent e) {
omits.clear();
tbmTraceDetail.filterOut(minPct, errOnly, omits);
}
}
public TraceDetailPanel(ErrorDetailView pnlStackTrace, MouseListener listener) {
this.pnlStackTrace = pnlStackTrace;
this.setLayout(new BorderLayout(0,0));
initToolbar();
initTable();
if (listener != null) {
tblTraceDetail.addMouseListener(listener);
}
}
private void initTable() {
JScrollPane scrTraceDetail = new JScrollPane();
this.add(scrTraceDetail, BorderLayout.CENTER);
tbmTraceDetail = new TraceDetailTableModel();
tblTraceDetail = new JTable(tbmTraceDetail);
tbmTraceDetail.configure(tblTraceDetail);
tblTraceDetail.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
tblTraceDetail.setAutoCreateColumnsFromModel(false);
tblTraceDetail.setAutoscrolls(false);
scrTraceDetail.setViewportView(tblTraceDetail);
tblTraceDetail.addMouseListener(new MouseAdapter() {
@Override public void mouseClicked(MouseEvent e) {
int selectedRow = tblTraceDetail.getSelectedRow();
if (selectedRow >= 0) {
TraceDetailPanel.this.pnlStackTrace.update(dataSet.getSymbols(),
tbmTraceDetail.getRecord(selectedRow));
ViewerTraceRecord record = tbmTraceDetail.getRecord(selectedRow);
switch (e.getButton()) {
case MouseEvent.BUTTON1: {
int x = e.getX() - getMethodCellOffset();
int refX = record.getLevel() * MethodCellRenderer.SINGLE_LEVEL;
if (x >= refX && x <= refX+16) {
record.toggleExpanded();
tbmTraceDetail.refresh();
}
}
break;
case MouseEvent.BUTTON2: {
omits.add(record.getClassName() + "." + record.getMethodName());
tbmTraceDetail.filterOut(minPct, errOnly, omits);
}
break;
case MouseEvent.BUTTON3: {
System.out.println("TODO implement popup menu");
}
break;
}
}
}
@Override public void mouseReleased(MouseEvent e) {
int r = tblTraceDetail.rowAtPoint(e.getPoint());
if (r >= 0 && r < tblTraceDetail.getRowCount()) {
tblTraceDetail.setRowSelectionInterval(r, r);
} else {
tblTraceDetail.clearSelection();
}
}
});
}
private void initToolbar() {
JToolBar tbDetailFilters = new JToolBar();
tbDetailFilters.setFloatable(false);
tbDetailFilters.setRollover(true);
ButtonGroup grpPctFilterButtons = new ButtonGroup();
JToggleButton btnPctFilterAll = new JToggleButton(new PctFilterAction("0", 0.0));
btnPctFilterAll.setFocusable(false);
btnPctFilterAll.setToolTipText("Show all method calls");
grpPctFilterButtons.add(btnPctFilterAll);
tbDetailFilters.add(btnPctFilterAll);
JToggleButton btnPctFilter01 = new JToggleButton(new PctFilterAction("01", 0.1));
btnPctFilter01.setFocusable(false);
btnPctFilter01.setToolTipText("Show calls that took > 0.1% of trace execution time");
grpPctFilterButtons.add(btnPctFilter01);
tbDetailFilters.add(btnPctFilter01);
JToggleButton btnPctFilter1 = new JToggleButton(new PctFilterAction("1", 1.0));
btnPctFilter1.setFocusable(false);
btnPctFilter1.setToolTipText("Show calls that took > 1% of trace execution time");
grpPctFilterButtons.add(btnPctFilter1);
tbDetailFilters.add(btnPctFilter1);
JToggleButton btnPctFilter10 = new JToggleButton(new PctFilterAction("10", 10.0));
btnPctFilter10.setFocusable(false);
btnPctFilter10.setToolTipText("Show calls that took > 10% of trace execution time");
grpPctFilterButtons.add(btnPctFilter10);
tbDetailFilters.add(btnPctFilter10);
tbDetailFilters.addSeparator();
ButtonGroup grpErrFilterButtons = new ButtonGroup();
btnErrorFilterErrors = new JButton(new ErrFilterAction("", "exception-thrown"));
btnErrorFilterErrors.setToolTipText("Show only methods with exceptions thrown");
btnErrorFilterErrors.setFocusable(false);
tbDetailFilters.add(btnErrorFilterErrors);
grpErrFilterButtons.add(btnErrorFilterErrors);
JButton btnClearFilters = new JButton(new ClearFiltersAction());
btnClearFilters.setToolTipText("Clear all exclusions (show all methods once again)");
btnClearFilters.setFocusable(false);
tbDetailFilters.add(btnClearFilters);
tbDetailFilters.addSeparator();
JButton btnSearchPrev = new JButton(new SearchAction("", "arrow-left-4", true));
btnSearchPrev.setFocusable(false);
btnSearchPrev.setToolTipText("Search previous occurence");
tbDetailFilters.add(btnSearchPrev);
JButton btnSearchNext = new JButton(new SearchAction("", "arrow-right-4", true));
btnSearchNext.setFocusable(false);
btnSearchNext.setToolTipText("Search previous occurence");
tbDetailFilters.add(btnSearchNext);
txtSearch = new JTextField(32);
tbDetailFilters.add(txtSearch);
this.add(tbDetailFilters, BorderLayout.NORTH);
}
private int getMethodCellOffset() {
int offs = 0;
for (int i = 0; i < TraceDetailTableModel.METHOD_COLUMN; i++) {
offs += tblTraceDetail.getColumnModel().getColumn(i).getWidth();
}
return offs;
}
public void setTrace(TraceDataSet dataSet, ViewerTraceRecord record) {
this.dataSet = dataSet;
tbmTraceDetail.setTrace(record);
}
}