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

org.jooq.debug.console.LoggerPane Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2009-2013, Lukas Eder, lukas.eder@gmail.com
 *                          Christopher Deckers, chrriis@gmail.com
 * All rights reserved.
 *
 * This software is licensed to you under the Apache License, Version 2.0
 * (the "License"); You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * . Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * . Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * . Neither the name "jOOQ" nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package org.jooq.debug.console;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.Popup;
import javax.swing.PopupFactory;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.TransferHandler;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;

import org.jooq.debug.Debugger;
import org.jooq.debug.LoggingListener;
import org.jooq.debug.QueryLog;
import org.jooq.debug.QueryMatcher;
import org.jooq.debug.QueryType;
import org.jooq.debug.ResultLog;
import org.jooq.debug.console.misc.InvisibleSplitPane;
import org.jooq.debug.console.misc.JTableX;
import org.jooq.debug.console.misc.RichTextTransferable;
import org.jooq.debug.console.misc.XTableColumnModel;
import org.jooq.debug.impl.Utils;

import org.fife.ui.rtextarea.RTextScrollPane;


/**
 * @author Christopher Deckers
 */
@SuppressWarnings({"serial", "hiding"})
public class LoggerPane extends JPanel {

    private static final String             LS                              = System.getProperty("line.separator");
    private static final SimpleDateFormat   TIMESTAMP_FORMAT                = new SimpleDateFormat("HH:mm:ss.SSS");
    private static final int                MAX_NUMBER_OF_ROWS              = 10000;

    private static final int                COLUMN_LINE                     = 0;
    private static final int                COLUMN_TYPE                     = 1;
    private static final int                COLUMN_THREAD                   = 2;
    private static final int                COLUMN_TIMESTAMP                = 3;
    private static final int                COLUMN_PS_PREPARATION_DURATION  = 4;
    private static final int                COLUMN_PS_BINDING_DURATION      = 5;
    private static final int                COLUMN_EXEC_TIME                = 6;
    private static final int                COLUMN_RS_LIFETIME              = 7;
    private static final int                COLUMN_RS_READ                  = 8;
    private static final int                COLUMN_RS_READ_ROWS             = 9;
    private static final int                COLUMN_DUPLICATION_COUNT        = 10;
    private static final int                COLUMN_QUERY                    = 11;
    private static final int                COLUMN_COUNT                    = COLUMN_QUERY + 1;

    private final ImageIcon                 INSERT_ICON                     = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlInsert16.png"));
    private final ImageIcon                 UPDATE_ICON                     = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlUpdate16.png"));
    private final ImageIcon                 DELETE_ICON                     = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlDelete16.png"));
    private final ImageIcon                 OTHER_ICON                      = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlOther16.png"));
    private final ImageIcon                 SELECT_ICON                     = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlSelect16.png"));

    // UI components

    private JTableX                         table;
    private SqlTextArea                     textArea;
    private JLabel                          loggerStatusLabel;
    private JButton                         loggerOnButton;
    private JButton                         loggerOffButton;
    private JToggleButton                   queryMatcherButton;
    private boolean                         isLogging;
    private boolean                         isReadQueryTypeDisplayed        = true;
    private boolean                         isWriteQueryTypeDisplayed       = true;
    private boolean                         isOtherQueryTypeDisplayed       = true;
    private boolean                         isScrollLocked;

    // Data and debug API

    private final Debugger                  debugger;
    private final LoggerPaneLoggingListener loggingListener;
    private List        queryDebuggingInfoList          = new ArrayList();
    private List        displayedQueryDebuggingInfoList = new ArrayList();
    private Map, Integer>      queriesToCountMap               = new HashMap, Integer>();

    public LoggerPane(Debugger debugger) {
        super(new BorderLayout());
        setBorder(BorderFactory.createEmptyBorder(2, 5, 0, 5));
        this.debugger = debugger;
        setOpaque(false);
        JPanel loggerHeaderPanel = new JPanel(new BorderLayout());
        loggerHeaderPanel.setOpaque(false);
        JToolBar loggerHeaderWestPanel = new JToolBar();
        loggerHeaderWestPanel.setFloatable(false);
        loggerHeaderWestPanel.setOpaque(false);
        loggingListener = new LoggerPaneLoggingListener();
        loggerOnButton = new JButton(new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/Paused16.png")));
        loggerOnButton.setOpaque(false);
        loggerOnButton.setFocusable(false);
        loggerOnButton.setToolTipText("Activate logging");
        loggerOnButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setLogging(true);
            }
        });
        loggerHeaderWestPanel.add(loggerOnButton);
        loggerOffButton = new JButton(new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/Running16.png")));
        loggerOffButton.setOpaque(false);
        loggerOffButton.setFocusable(false);
        loggerOffButton.setToolTipText("Deactivate logging");
        loggerOffButton.setVisible(false);
        loggerOffButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                setLogging(false);
                loggerOnButton.requestFocus();
            }
        });
        loggerHeaderWestPanel.add(loggerOffButton);
        queryMatcherButton = new JToggleButton(new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/Filter16.png")));
        queryMatcherButton.setOpaque(false);
        queryMatcherButton.setFocusable(false);
        queryMatcherButton.setToolTipText("Filter incoming queries");
        adjustQueryMatcherButton();
        queryMatcherButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                queryMatcherButton.setSelected(true);
                new QueryMatchersDialogBox(LoggerPane.this, LoggerPane.this.debugger, loggingListener).setVisible(true);
                adjustQueryMatcherButton();
            }
        });
        loggerHeaderWestPanel.add(queryMatcherButton);
        loggerHeaderPanel.add(loggerHeaderWestPanel, BorderLayout.WEST);
        JPanel loggerHeaderCenterPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
        loggerHeaderCenterPanel.setOpaque(false);
        JCheckBox loggerThreadCheckBox = new JCheckBox("Threads", true);
        loggerThreadCheckBox.setOpaque(false);
        loggerThreadCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isThreadDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_THREAD), isThreadDisplayed);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(loggerThreadCheckBox);
        JCheckBox loggerTimestampCheckBox = new JCheckBox("Timestamps", true);
        loggerTimestampCheckBox.setOpaque(false);
        loggerTimestampCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isTimestampDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_TIMESTAMP), isTimestampDisplayed);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(loggerTimestampCheckBox);
        JCheckBox preparedStatementDataCheckBox = new JCheckBox("PS Data", true);
        preparedStatementDataCheckBox.setOpaque(false);
        preparedStatementDataCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isPreparedStatementDataShown = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_PS_PREPARATION_DURATION), isPreparedStatementDataShown);
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_PS_BINDING_DURATION), isPreparedStatementDataShown);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(preparedStatementDataCheckBox);
        JCheckBox loggerDurationCheckBox = new JCheckBox("Exec Time", true);
        loggerDurationCheckBox.setOpaque(false);
        loggerDurationCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isDurationDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_EXEC_TIME), isDurationDisplayed);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(loggerDurationCheckBox);
        JCheckBox resultSetDataCheckBox = new JCheckBox("RS Data", true);
        resultSetDataCheckBox.setOpaque(false);
        resultSetDataCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isResultSetDataShown = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_RS_LIFETIME), isResultSetDataShown);
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_RS_READ), isResultSetDataShown);
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_RS_READ_ROWS), isResultSetDataShown);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(resultSetDataCheckBox);
        JCheckBox duplicationCountCheckBox = new JCheckBox("Duplication", true);
        duplicationCountCheckBox.setOpaque(false);
        duplicationCountCheckBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean isDuplicationCountShown = e.getStateChange() == ItemEvent.SELECTED;
                XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel();
                columnModel.setColumnVisible(columnModel.getColumnByModelIndex(COLUMN_DUPLICATION_COUNT), isDuplicationCountShown);
                table.adjustLastColumn();
            }
        });
        loggerHeaderCenterPanel.add(duplicationCountCheckBox);
        loggerHeaderPanel.add(loggerHeaderCenterPanel, BorderLayout.CENTER);
        JToolBar loggerHeaderEastPanel = new JToolBar();
        loggerHeaderEastPanel.setFloatable(false);
        loggerHeaderEastPanel.setOpaque(false);
        JToggleButton loggerReadQueryTypeToggleButton = new JToggleButton(SELECT_ICON, isReadQueryTypeDisplayed);
        loggerReadQueryTypeToggleButton.setOpaque(false);
        loggerReadQueryTypeToggleButton.setFocusable(false);
        loggerReadQueryTypeToggleButton.setToolTipText("Show/hide read queries");
        loggerReadQueryTypeToggleButton.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                isReadQueryTypeDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                refreshRows();
            }
        });
        loggerHeaderEastPanel.add(loggerReadQueryTypeToggleButton);
        JToggleButton loggerWriteQueryTypeToggleButton = new JToggleButton(UPDATE_ICON, isWriteQueryTypeDisplayed);
        loggerWriteQueryTypeToggleButton.setOpaque(false);
        loggerWriteQueryTypeToggleButton.setFocusable(false);
        loggerWriteQueryTypeToggleButton.setToolTipText("Show/hide modification queries");
        loggerWriteQueryTypeToggleButton.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                isWriteQueryTypeDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                refreshRows();
            }
        });
        loggerHeaderEastPanel.add(loggerWriteQueryTypeToggleButton);
        JToggleButton loggerOtherQueryTypeToggleButton = new JToggleButton(OTHER_ICON, isOtherQueryTypeDisplayed);
        loggerOtherQueryTypeToggleButton.setOpaque(false);
        loggerOtherQueryTypeToggleButton.setFocusable(false);
        loggerOtherQueryTypeToggleButton.setToolTipText("Show/hide other types of queries");
        loggerOtherQueryTypeToggleButton.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                isOtherQueryTypeDisplayed = e.getStateChange() == ItemEvent.SELECTED;
                refreshRows();
            }
        });
        loggerHeaderEastPanel.add(loggerOtherQueryTypeToggleButton);
        JButton loggerClearButton = new JButton(new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/Clear16.png")));
        loggerClearButton.setOpaque(false);
        loggerClearButton.setFocusable(false);
        loggerClearButton.setToolTipText("Clear collected data");
        loggerClearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                queryDebuggingInfoList.clear();
                textArea.setText("");
                int originalRowCount = displayedQueryDebuggingInfoList.size();
                displayedQueryDebuggingInfoList.clear();
                queriesToCountMap.clear();
                if(originalRowCount > 0) {
                    ((AbstractTableModel)table.getModel()).fireTableRowsDeleted(0, originalRowCount - 1);
                }
                updateStatusLabel();
            }
        });
        loggerHeaderEastPanel.add(new JToolBar.Separator());
        loggerHeaderEastPanel.add(loggerClearButton);
        JToggleButton scrollLockToggleButton = new JToggleButton(new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/LockScroll16.png")));
        scrollLockToggleButton.setFocusable(false);
        scrollLockToggleButton.setToolTipText("Scroll Lock");
        scrollLockToggleButton.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                isScrollLocked = e.getStateChange() == ItemEvent.SELECTED;
            }
        });
        loggerHeaderEastPanel.add(scrollLockToggleButton);
        loggerHeaderPanel.add(loggerHeaderEastPanel, BorderLayout.EAST);
        add(loggerHeaderPanel, BorderLayout.NORTH);
        table = new JTableX(new AbstractTableModel() {
            @Override
            public Object getValueAt(int rowIndex, int columnIndex) {
                QueryDebuggingInfo queryDebuggingInfo = displayedQueryDebuggingInfoList.get(rowIndex);
                switch(columnIndex) {
                    case COLUMN_LINE: {
                        return rowIndex + 1;
                    }
                    case COLUMN_TYPE: {
                        return queryDebuggingInfo.getQueryType();
                    }
                    case COLUMN_THREAD: {
                        return queryDebuggingInfo.getThreadName() + " [" + queryDebuggingInfo.getThreadId() + "]";
                    }
                    case COLUMN_TIMESTAMP: {
                        return TIMESTAMP_FORMAT.format(new Date(queryDebuggingInfo.getTimestamp()));
                    }
                    case COLUMN_PS_PREPARATION_DURATION: {
                        Long duration = queryDebuggingInfo.getPreparedStatementPreparationDuration();
                        return duration == null? null: duration;
                    }
                    case COLUMN_PS_BINDING_DURATION: {
                        Long duration = queryDebuggingInfo.getPreparedStatementBindingDuration();
                        return duration == null? null: duration;
                    }
                    case COLUMN_EXEC_TIME: {
                        long duration = queryDebuggingInfo.getExecutionDuration();
                        return duration < 0? null: duration;
                    }
                    case COLUMN_RS_LIFETIME: {
                        ResultLog rsData = queryDebuggingInfo.getResultSetLoggingData();
                        return rsData == null? null: rsData.getLifeTime();
                    }
                    case COLUMN_RS_READ: {
                        ResultLog rsData = queryDebuggingInfo.getResultSetLoggingData();
                        return rsData == null? null: rsData.getReadCount();
                    }
                    case COLUMN_RS_READ_ROWS: {
                        ResultLog rsData = queryDebuggingInfo.getResultSetLoggingData();
                        return rsData == null? null: rsData.getReadRows();
                    }
                    case COLUMN_DUPLICATION_COUNT: {
                        return queryDebuggingInfo.getDuplicationCount();
                    }
                    case COLUMN_QUERY: {
                        StringBuilder querySB = new StringBuilder();
                        String[] queries = queryDebuggingInfo.getQueries();
                        for(int i=0; i 0) {
                                querySB.append(LS);
                            }
                            String s = queries[i];
                            querySB.append(s.trim());
                        }
                        return querySB.toString();
                    }
                }
                return null;
            }

            @Override
            public int getRowCount() {
                return displayedQueryDebuggingInfoList.size();
            }

            @Override
            public int getColumnCount() {
                return COLUMN_COUNT;
            }

            @Override
            public String getColumnName(int column) {
                switch(column) {
                    case COLUMN_LINE:
                        return "Line";
                    case COLUMN_TYPE:
                        return "Type";
                    case COLUMN_THREAD:
                        return "Thread";
                    case COLUMN_TIMESTAMP:
                        return "Timestamp";
                    case COLUMN_PS_PREPARATION_DURATION:
                        return "PS preparation (ms)";
                    case COLUMN_PS_BINDING_DURATION:
                        return "PS binding (ms)";
                    case COLUMN_EXEC_TIME:
                        return "Exec time (ms)";
                    case COLUMN_RS_LIFETIME:
                        return "RS lifetime (ms)";
                    case COLUMN_RS_READ:
                        return "RS read";
                    case COLUMN_RS_READ_ROWS:
                        return "RS rows";
                    case COLUMN_DUPLICATION_COUNT:
                        return "Duplic.";
                    case COLUMN_QUERY:
                        return "Query";
                }
                return null;
            }

            @Override
            public Class getColumnClass(int columnIndex) {
                switch(columnIndex) {
                    case COLUMN_LINE: return Integer.class;
                    case COLUMN_TYPE: return QueryType.class;
                    case COLUMN_PS_PREPARATION_DURATION: return Long.class;
                    case COLUMN_PS_BINDING_DURATION: return Long.class;
                    case COLUMN_EXEC_TIME: return Long.class;
                    case COLUMN_RS_LIFETIME: return Long.class;
                    case COLUMN_RS_READ: return Integer.class;
                    case COLUMN_RS_READ_ROWS: return Integer.class;
                    case COLUMN_DUPLICATION_COUNT: return Integer.class;
                    case COLUMN_QUERY: return String.class;
                }
                return super.getColumnClass(columnIndex);
            }

        });
        registerTooltip();
        table.setAutoCreateRowSorter(true);
        table.getRowSorter().setSortKeys(Arrays.asList(new RowSorter.SortKey(COLUMN_LINE, SortOrder.ASCENDING)));
        XTableColumnModel columnModel = new XTableColumnModel();
        table.setColumnModel(columnModel);
        table.createDefaultColumnsFromModel();
        table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        columnModel.getColumnByModelIndex(COLUMN_LINE).setPreferredWidth(30);
        columnModel.getColumnByModelIndex(COLUMN_TYPE).setPreferredWidth(20);
        columnModel.getColumnByModelIndex(COLUMN_TIMESTAMP).setPreferredWidth(80);
        columnModel.getColumnByModelIndex(COLUMN_THREAD).setPreferredWidth(150);
        columnModel.getColumnByModelIndex(COLUMN_DUPLICATION_COUNT).setPreferredWidth(40);
        table.setColumnSelectionAllowed(true);
        table.setFillsViewportHeight(true);
//        ToolTipManager.sharedInstance().registerComponent(table);
        table.setDefaultRenderer(QueryType.class, new DefaultTableCellRenderer() {
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
                if(c instanceof JLabel) {
                    Icon icon = null;
                    int modelRow = table.convertRowIndexToModel(row);
                    QueryDebuggingInfo queryDebuggingInfo = displayedQueryDebuggingInfoList.get(modelRow);
                    switch(queryDebuggingInfo.getQueryType()) {
                        case SELECT: icon = SELECT_ICON; break;
                        case INSERT: icon = INSERT_ICON; break;
                        case UPDATE: icon = UPDATE_ICON; break;
                        case DELETE: icon = DELETE_ICON; break;
                        case OTHER: icon = OTHER_ICON; break;
                    }
                    ((JLabel)c).setText(null);
                    ((JLabel)c).setIcon(icon);
                }
                return c;
            }
        });
        table.setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
            private Pattern pattern = Pattern.compile("[\\t\\n\\x0B\\f\\r]+");
            @Override
            public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
                // Convert special whitespace characters to a space.
                if(value != null) {
                    value = pattern.matcher((String)value).replaceAll(" ");
                }
                return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            }
        });
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                maybeShowPopup(e);
            }
            @Override
            public void mouseReleased(MouseEvent e) {
                maybeShowPopup(e);
            }
            private void maybeShowPopup(MouseEvent e) {
                if(e.isPopupTrigger()) {
                    Point location = e.getPoint();
                    int row = table.rowAtPoint(location);
                    if(row < 0) {
                        return;
                    }
                    int column = table.columnAtPoint(location);
                    if(column < 0) {
                        return;
                    }
                    if(!table.isCellSelected(row, column)) {
                        ListSelectionModel selectionModel = table.getSelectionModel();
                        selectionModel.clearSelection();
                        selectionModel.addSelectionInterval(row, row);
                    }
                    JPopupMenu popupMenu = new JPopupMenu();
                    int[] selectedRows = table.getSelectedRows();
                    final QueryDebuggingInfo[] selectedQueryDebuggingInfos = new QueryDebuggingInfo[selectedRows.length];
                    for(int i=0; i= 0 && table.getSelectedColumn() >= 0) {
                        JMenuItem copyCellsToClipboardMenuItem = new JMenuItem("Copy Selected Cells to Clipboard");
//                        copyCellsToClipboardMenuItem.setAccelerator(keyStroke);
                        copyCellsToClipboardMenuItem.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                table.getTransferHandler().exportToClipboard(table, Toolkit.getDefaultToolkit().getSystemClipboard(), TransferHandler.COPY);
                            }
                        });
                        popupMenu.add(copyCellsToClipboardMenuItem);
                    }
                    if(selectedQueryDebuggingInfos.length > 0) {
                        JMenuItem copyToClipboardMenuItem = new JMenuItem("Copy " + (selectedQueryDebuggingInfos.length > 1? selectedQueryDebuggingInfos.length + " ": "") + "Query Data to Clipboard");
                        copyToClipboardMenuItem.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                copyToClipboard(selectedQueryDebuggingInfos);
                            }
                        });
                        popupMenu.add(copyToClipboardMenuItem);
                    }
                    if(displayedQueryDebuggingInfoList.size() > 0) {
                        JMenuItem copyAllToClipboardMenuItem = new JMenuItem("Copy All Query Data (" + displayedQueryDebuggingInfoList.size() + ") to Clipboard");
                        copyAllToClipboardMenuItem.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                copyToClipboard(displayedQueryDebuggingInfoList.toArray(new QueryDebuggingInfo[0]));
                            }
                        });
                        popupMenu.add(copyAllToClipboardMenuItem);
                    }
                    if(selectedQueryDebuggingInfos.length == 1) {
                        if(popupMenu.getComponentCount() > 0) {
                            popupMenu.addSeparator();
                        }
                        JMenuItem copyStackToClipboardMenuItem = new JMenuItem("Copy Call Stack to Clipboard");
                        copyStackToClipboardMenuItem.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                StringWriter sw = new StringWriter();
                                selectedQueryDebuggingInfos[0].getThrowable().printStackTrace(new PrintWriter(sw));
                                Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                                clipboard.setContents(new StringSelection(sw.toString()), null);
                            }
                        });
                        popupMenu.add(copyStackToClipboardMenuItem);
                        JMenuItem dumpStackMenuItem = new JMenuItem("Dump Call Stack");
                        dumpStackMenuItem.addActionListener(new ActionListener() {
                            @Override
                            public void actionPerformed(ActionEvent e) {
                                selectedQueryDebuggingInfos[0].getThrowable().printStackTrace();
                            }
                        });
                        popupMenu.add(dumpStackMenuItem);
                    }
                    if(popupMenu.getComponentCount() > 0) {
                        popupMenu.show(table, e.getX(), e.getY());
                    }
                }
            }
        });
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if(e.getValueIsAdjusting()) {
                    return;
                }
                int[] selectedRows = table.getSelectedRows();
                String text;
                if(selectedRows.length > 30) {
                    text = "(Too many selected rows)";
                } else {
                    StringBuilder sb = new StringBuilder();
                    for(int row: selectedRows) {
                        row = table.convertRowIndexToModel(row);
                        QueryDebuggingInfo queryDebuggingInfo = displayedQueryDebuggingInfoList.get(row);
                        for(String query: queryDebuggingInfo.getQueries()) {
                            sb.append(query.trim()).append(LS);
                        }
                    }
                    text = sb.toString();
                }
                if(!text.equals(textArea.getText())) {
                    textArea.setText(text);
                    textArea.setCaretPosition(0);
                }
                updateStatusLabel();
            }
        });
        textArea = new SqlTextArea();
        final JSplitPane splitPane = new InvisibleSplitPane(JSplitPane.VERTICAL_SPLIT, true, new JScrollPane(table), new RTextScrollPane(textArea));
        splitPane.setResizeWeight(1);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                splitPane.setDividerLocation(splitPane.getHeight() - 100);
            }
        });
        add(splitPane, BorderLayout.CENTER);
        loggerStatusLabel = new JLabel();
        updateStatusLabel();
        add(loggerStatusLabel, BorderLayout.SOUTH);
        preparedStatementDataCheckBox.setSelected(false);
        resultSetDataCheckBox.setSelected(false);
        duplicationCountCheckBox.setSelected(false);
        loggerTimestampCheckBox.setSelected(false);
        loggerDurationCheckBox.setSelected(false);
        loggerThreadCheckBox.setSelected(false);
    }

    private void adjustQueryMatcherButton() {
        QueryMatcher[] matchers = loggingListener.getMatchers();
        queryMatcherButton.setSelected(matchers != null);
    }

    private void refreshRows() {
        int originalRowCount = displayedQueryDebuggingInfoList.size();
        displayedQueryDebuggingInfoList.clear();
        queriesToCountMap.clear();
        textArea.setText("");
        if(originalRowCount > 0) {
            ((AbstractTableModel)table.getModel()).fireTableRowsDeleted(0, originalRowCount - 1);
        }
        for(QueryDebuggingInfo queryDebuggingInfo: queryDebuggingInfoList) {
            addDisplayedRow(queryDebuggingInfo);
        }
        int displayedRowCount = displayedQueryDebuggingInfoList.size();
        if(displayedRowCount > 0) {
            ((AbstractTableModel)table.getModel()).fireTableRowsInserted(0, displayedRowCount - 1);
        }
        updateStatusLabel();
    }

    private void addRow(QueryDebuggingInfo queryDebuggingInfo) {
        if(queryDebuggingInfoList.size() == MAX_NUMBER_OF_ROWS) {
            QueryDebuggingInfo discaredDebuggingInfo = queryDebuggingInfoList.remove(0);
            if(displayedQueryDebuggingInfoList.size() > 0 && displayedQueryDebuggingInfoList.get(0) == discaredDebuggingInfo) {
                displayedQueryDebuggingInfoList.remove(0);
                for(int i=displayedQueryDebuggingInfoList.size()-1; i>=0; i--) {
                    displayedQueryDebuggingInfoList.get(i).setDisplayedRow(i);
                }
                ((AbstractTableModel)table.getModel()).fireTableRowsDeleted(0, 0);
            }
        }
        queryDebuggingInfoList.add(queryDebuggingInfo);
        addDisplayedRow(queryDebuggingInfo);
        int displayedRow = queryDebuggingInfo.getDisplayedRow();
        if(displayedRow >= 0) {
            ((AbstractTableModel)table.getModel()).fireTableRowsInserted(displayedRow, displayedRow);
            if(!isScrollLocked) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        // Sort the line column if it is the primary sort key in ascending order.
                        List sortKeys = table.getRowSorter().getSortKeys();
                        if(sortKeys.size() >= 1) {
                            SortKey sortKey = sortKeys.get(0);
                            if(sortKey.getColumn() == COLUMN_LINE && sortKey.getSortOrder() == SortOrder.ASCENDING) {
                                table.scrollRectToVisible(new Rectangle(0, table.getHeight() - 1, 1, 1));
                            }
                        }
                    }
                });
            }
        }
        updateStatusLabel();
    }

    private void addDisplayedRow(QueryDebuggingInfo queryDebuggingInfo) {
        boolean isDisplayed = false;
        switch(queryDebuggingInfo.getQueryType()) {
            case SELECT: isDisplayed = isReadQueryTypeDisplayed; break;
            case INSERT:
            case UPDATE:
            case DELETE:
                isDisplayed = isWriteQueryTypeDisplayed;
                break;
            case OTHER: isDisplayed = isOtherQueryTypeDisplayed; break;
        }
        int displayedRow = -1;
        if(isDisplayed) {
            List queryList = Arrays.asList(queryDebuggingInfo.getQueries());
            Integer count = queriesToCountMap.get(queryList);
            if(count == null) {
                count = 1;
            } else {
                count++;
            }
            queriesToCountMap.put(queryList, count);
            displayedRow = displayedQueryDebuggingInfoList.size();
            queryDebuggingInfo.setDuplicationCount(count);
            displayedQueryDebuggingInfoList.add(queryDebuggingInfo);
        }
        queryDebuggingInfo.setDisplayedRow(displayedRow);
    }

    private void updateRow(QueryDebuggingInfo queryDebuggingInfo) {
        int displayedRow = queryDebuggingInfo.getDisplayedRow();
        if(displayedRow >= 0) {
            ((AbstractTableModel)table.getModel()).fireTableRowsUpdated(displayedRow, displayedRow);
        }
    }

    private static class QueryDebuggingInfo {
        private long timestamp;
        private QueryLog queryLog;
        private Throwable throwable;
        private int duplicationCount;
        public QueryDebuggingInfo(long timestamp, QueryLog queryLog) {
            this.timestamp = timestamp;
            this.queryLog = queryLog;
            this.throwable = new Exception("Query Stack trace");
            throwable.setStackTrace(queryLog.getCallerStackTraceElements());
        }
        public long getTimestamp() {
            return timestamp;
        }
        public QueryLog getQueryLoggingData() {
            return queryLog;
        }
        public Long getPreparedStatementPreparationDuration() {
            return queryLog.getPreparedStatementPreparationDuration();
        }
        public Long getPreparedStatementBindingDuration() {
            return queryLog.getPreparedStatementBindingDuration();
        }
        public long getExecutionDuration() {
            return queryLog.getExecutionDuration();
        }
        public QueryType getQueryType() {
            return queryLog.getQueryInfo().getQueryType();
        }
        public String[] getQueries() {
            String parameterDescription = queryLog.getQueryInfo().getParameterDescription();
            String[] queries = queryLog.getQueryInfo().getQueries();
            if(parameterDescription != null) {
                return new String[] {queries[0] + " -> " + parameterDescription};
            }
            return queries;
        }
        public Throwable getThrowable() {
            return throwable;
        }
        public String getThreadName() {
            return queryLog.getQueryInfo().getThreadName();
        }
        public long getThreadId() {
            return queryLog.getQueryInfo().getThreadID();
        }
        public void setDuplicationCount(int duplicationCount) {
            this.duplicationCount = duplicationCount;
        }
        public int getDuplicationCount() {
            return duplicationCount;
        }
        private ResultLog resultLog;
        public void setResultSetLoggingData(ResultLog resultLog) {
            this.resultLog = resultLog;
        }
        public ResultLog getResultSetLoggingData() {
            return resultLog;
        }
        private int displayedRow = -1;
        public int getDisplayedRow() {
            return displayedRow;
        }
        public void setDisplayedRow(int displayedRow) {
            this.displayedRow = displayedRow;
        }
    }

    public void setLogging(boolean isLogging) {
        if (this.isLogging == isLogging) {
            return;
        }

        this.isLogging = isLogging;

        loggerOnButton.setVisible(!isLogging);
        loggerOffButton.setVisible(isLogging);
        if (isLogging) {
            debugger.setLoggingListener(loggingListener);
        }
        else {
            debugger.setLoggingListener(null);
        }
    }

    class LoggerPaneLoggingListener implements LoggingListener {
        private QueryMatcher[] matchers;

        @Override
        public QueryMatcher[] getMatchers() {
            return matchers;
        }

        public void setMatchers(QueryMatcher[] matchers) {
            this.matchers = matchers;
        }

        @Override
        public void logQuery(QueryLog queryLog) {
            debugQueries(new QueryDebuggingInfo(System.currentTimeMillis(), queryLog));
        }

        public void debugQueries(final QueryDebuggingInfo queryDebuggingInfo) {
            if (!SwingUtilities.isEventDispatchThread()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        debugQueries(queryDebuggingInfo);
                    }
                });
                return;
            }
            addRow(queryDebuggingInfo);
        }

        @Override
        public void logResult(final ResultLog resultLog) {
            if (!SwingUtilities.isEventDispatchThread()) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        logResult(resultLog);
                    }
                });
                return;
            }
            for (int i = queryDebuggingInfoList.size() - 1; i >= 0; i--) {
                QueryDebuggingInfo queryDebuggingInfo = queryDebuggingInfoList.get(i);
                if (queryDebuggingInfo.getQueryLoggingData().getID() == resultLog.getQueryLogId()) {
                    queryDebuggingInfo.setResultSetLoggingData(resultLog);
                    XTableColumnModel columnModel = (XTableColumnModel) table.getColumnModel();
                    boolean isResultSetDataShown = columnModel.isColumnVisible(columnModel.getColumnByModelIndex(COLUMN_RS_LIFETIME));
                    if (isResultSetDataShown) {
                        updateRow(queryDebuggingInfo);
                    }
                    break;
                }
            }
        }
    }

    private void updateStatusLabel() {
        int size = queryDebuggingInfoList.size();
        int displayedCount = displayedQueryDebuggingInfoList.size();
        String text;
        if(displayedCount == size) {
            text = size + " queries";
        } else {
            text = displayedCount + "/" + size + " queries";
        }
        int selectedRowCount = table.getSelectedRowCount();
        if(selectedRowCount > 0) {
            text = text + " - " + selectedRowCount + " selected rows";
            if(selectedRowCount > 1) {
                int[] selectedColumns = table.getSelectedColumns();
                if(selectedColumns.length == 1) {
                    int column = table.convertColumnIndexToModel(selectedColumns[0]);
                    switch(column) {
                        case COLUMN_EXEC_TIME:
                        case COLUMN_RS_LIFETIME:
                        case COLUMN_RS_READ:
                        case COLUMN_RS_READ_ROWS:
                            int[] selectedRows = table.getSelectedRows();
                            TableModel model = table.getModel();
                            long sum = 0;
                            boolean isValid = true;
                            for(int row: selectedRows) {
                                row = table.convertRowIndexToModel(row);
                                Number number = (Number)model.getValueAt(row, column);
                                if(number == null) {
                                    isValid = false;
                                    break;
                                }
                                sum += number.longValue();
                            }
                            if(isValid) {
                                text += " - sum: " + sum;
                            }
                            break;
                    }
                }
            }
        }
        loggerStatusLabel.setText(text);
    }

    private String getStackTrace(final QueryDebuggingInfo queryDebuggingInfo) {
        StackTraceElement[] stackTraceElements = queryDebuggingInfo.getThrowable().getStackTrace();
        StringBuilder sb = new StringBuilder();
        // TODO: adjust number of levels to ignore before and after.
        for(int i=1; i 0) {
                sb.append('\n');
            }
            sb.append(stackTraceElement);
        }
        return sb.toString();
    }

    private void copyToClipboard(QueryDebuggingInfo[] queryDebuggingInfos) {
        StringBuilder stringSB = new StringBuilder();
        StringBuilder htmlSB = new StringBuilder();
        htmlSB.append("\n\n\n");
        htmlSB.append("" +
                "" +
                "" +
                "" +
                "" +
                "" +
                "" +
                "" +
//                "" +
                "" +
                "" +
                "" +
                "\n");
        for(QueryDebuggingInfo queryDebuggingInfo: queryDebuggingInfos) {
            ResultLog resultSetData = queryDebuggingInfo.getResultSetLoggingData();
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
//            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
            htmlSB.append("\n");
        }
        htmlSB.append("
TypeThread nameThread IDTimestampExec time (ms)RS lifetime (ms)RS readRS writeRS rows readQueryStack trace
"); htmlSB.append(queryDebuggingInfo.getQueryType()); htmlSB.append(""); htmlSB.append(queryDebuggingInfo.getThreadName()); htmlSB.append(""); htmlSB.append(queryDebuggingInfo.getThreadId()); htmlSB.append(""); htmlSB.append(TIMESTAMP_FORMAT.format(new Date(queryDebuggingInfo.getTimestamp()))); htmlSB.append(""); htmlSB.append(queryDebuggingInfo.getExecutionDuration()); htmlSB.append(""); htmlSB.append(resultSetData == null? "": resultSetData.getLifeTime()); htmlSB.append(""); htmlSB.append(resultSetData == null? "": resultSetData.getReadCount()); htmlSB.append(""); // htmlSB.append(resultSetData == null? "": resultSetData.getWriteCount()); // htmlSB.append(""); htmlSB.append(resultSetData == null? "": resultSetData.getReadRows()); htmlSB.append(""); String[] queries = queryDebuggingInfo.getQueries(); for(int i=0; i 0) { htmlSB.append("\n"); } htmlSB.append(query); stringSB.append(query.trim() + LS); } htmlSB.append(""); htmlSB.append(getStackTrace(queryDebuggingInfo)); htmlSB.append("
\n\n"); Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); clipboard.setContents(new RichTextTransferable(htmlSB.toString(), stringSB.toString()), null); } private void registerTooltip() { class TableTipListener extends MouseInputAdapter implements TableModelListener { private Timer enterTimer; public TableTipListener() { enterTimer = new Timer(750, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { processTip(); } }); enterTimer.setRepeats(false); } private Point point; @Override public void mouseEntered(MouseEvent e) { point = SwingUtilities.convertPoint(table, e.getPoint(), table.getParent()); enterTimer.start(); } @Override public void mouseMoved(MouseEvent e) { point = SwingUtilities.convertPoint(table, e.getPoint(), table.getParent()); if(tip == null) { enterTimer.restart(); } else { processTip(); } } @Override public void mousePressed(MouseEvent e) { enterTimer.stop(); processTip(null); } private void processTip() { String text = getMultilineTooltip(SwingUtilities.convertPoint(table.getParent(), point, table)); processTip(text); } @Override public void mouseExited(MouseEvent e) { point = null; enterTimer.stop(); processTip(null); } private String lastText; private Popup tip; private void processTip(String text) { if(Utils.equals(lastText, text)) { return; } lastText = text; if(tip != null) { tip.hide(); tip = null; } if(text != null) { PopupFactory popupFactory = PopupFactory.getSharedInstance(); JTextArea textContent = new JTextArea(text); textContent.setFont(UIManager.getFont("ToolTip.font")); textContent.setBackground(UIManager.getColor("ToolTip.background")); textContent.setForeground(UIManager.getColor("ToolTip.foreground")); textContent.setBorder(UIManager.getBorder("ToolTip.border")); Point location = new Point(point); SwingUtilities.convertPointToScreen(location, table.getParent()); GraphicsConfiguration gc = table.getGraphicsConfiguration(); Rectangle sBounds = gc.getBounds(); Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc); sBounds.x += screenInsets.left; sBounds.y += screenInsets.top; sBounds.width -= screenInsets.left + screenInsets.right; sBounds.height -= screenInsets.top + screenInsets.bottom; Dimension tipSize = textContent.getPreferredSize(); // tipSize.height = Math.min(tipSize.height, 500); textContent.setPreferredSize(tipSize); location.x += 20; location.x = Math.min(location.x, sBounds.x + sBounds.width - tipSize.width); if(location.y + tipSize.height > sBounds.y + sBounds.height && location.y - 40 - tipSize.height >= sBounds.y) { location.y -= 40 + tipSize.height; } location.y += 20; tip = popupFactory.getPopup(null, textContent, location.x, location.y); tip.show(); } } @Override public void tableChanged(TableModelEvent e) { if(tip != null) { processTip(); } } }; TableTipListener tableTipListener = new TableTipListener(); table.addMouseListener(tableTipListener); table.addMouseMotionListener(tableTipListener); table.getModel().addTableModelListener(tableTipListener); } private String getMultilineTooltip(Point p) { int row = table.rowAtPoint(p); if(row < 0) { return null; } int column = table.columnAtPoint(p); if(column < 0) { return null; } row = table.convertRowIndexToModel(row); column = table.convertColumnIndexToModel(column); if(column != COLUMN_QUERY) { return null; } final QueryDebuggingInfo queryDebuggingInfo = displayedQueryDebuggingInfoList.get(row); if(queryDebuggingInfo != null) { return queryDebuggingInfo.getThreadName() + " [" + queryDebuggingInfo.getThreadId() + "]\n" + getStackTrace(queryDebuggingInfo); } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy