de.codesourcery.threadwatcher.ui.StatisticsPanel.java Source code

Java tutorial

Introduction

Here is the source code for de.codesourcery.threadwatcher.ui.StatisticsPanel.java

Source

/**
 * Copyright 2013 Tobias Gierke <tobias.gierke@code-sourcery.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.codesourcery.threadwatcher.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;

import org.apache.commons.lang.ObjectUtils;

import de.codesourcery.threadwatcher.FileReader;
import de.codesourcery.threadwatcher.FileReader.FileVisitor;
import de.codesourcery.threadwatcher.HiResInterval;
import de.codesourcery.threadwatcher.PerThreadStatistics;
import de.codesourcery.threadwatcher.ThreadEvent;
import de.codesourcery.threadwatcher.ui.PercentageBar.IPercentageProvider;

public final class StatisticsPanel extends JPanel {
    private static final String[] COLUMN_NAMES;

    static {
        List<String> columnNames = new ArrayList<>();
        columnNames.add("Thread");
        columnNames.add("States");
        for (LegendItem item : UIConstants.LEGEND_ITEMS) {
            columnNames.add(item.title + " %");
        }
        COLUMN_NAMES = new String[columnNames.size()];
        for (int i = 0; i < columnNames.size(); i++) {
            COLUMN_NAMES[i] = columnNames.get(i);
        }
    }

    private static final int COLUMN_THREAD_NAME = 0;
    private static final int COLUMN_PERCENTAGE_BAR = 1;

    private FileReader reader;
    private HiResInterval interval;

    private final List<TableEntry> tableData = new ArrayList<>();
    private final MyTableModel tableModel = new MyTableModel();
    private final JTable table = new JTable(tableModel);

    private final MouseAdapter mouseListener = new MouseAdapter() {
        public void mouseMoved(java.awt.event.MouseEvent e) {
            int col = table.columnAtPoint(e.getPoint());
            if (col == COLUMN_PERCENTAGE_BAR) {
                int row = table.rowAtPoint(e.getPoint());
                if (row >= 0 && row < tableData.size()) {
                    final TableEntry entry = tableData.get(row);
                    final Rectangle rectangle = table.getCellRect(row, col, false);
                    Point translated = new Point(e.getPoint().x - rectangle.x, e.getPoint().y - rectangle.y);
                    final LegendItemProvider value = (LegendItemProvider) entry.percentageBar
                            .getValueForPoint(translated);
                    String tooltip = null;
                    if (value != null) {
                        tooltip = value.item.title + " : " + formatPercentageValue(value.getPercentageValue())
                                + " %";
                    }
                    if (!ObjectUtils.equals(tooltip, entry.percentageBar.getToolTipText())) {
                        entry.percentageBar.setToolTipText(tooltip);
                    }
                }
            }
        }
    };

    protected final class MyTableModel implements TableModel {

        private final List<TableModelListener> listeners = new ArrayList<>();

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

        @Override
        public int getColumnCount() {
            return COLUMN_NAMES.length;
        }

        @Override
        public String getColumnName(int columnIndex) {
            return COLUMN_NAMES[columnIndex];
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return TableEntry.class;
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return false;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return tableData.get(rowIndex);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            /* NOP */ }

        public void fireDataChanged() {
            final TableModelEvent ev = new TableModelEvent(this);
            for (TableModelListener l : listeners) {
                l.tableChanged(ev);
            }
        }

        @Override
        public void addTableModelListener(TableModelListener l) {
            listeners.add(l);
        }

        @Override
        public void removeTableModelListener(TableModelListener l) {
            listeners.remove(l);
        }
    }

    protected static final class TableEntry {
        public final PerThreadStatistics statistics;
        public final PercentageBar percentageBar;

        public TableEntry(PerThreadStatistics statistics, PercentageBar percentageBar) {
            this.statistics = statistics;
            this.percentageBar = percentageBar;
        }
    }

    protected final class CellRenderer extends DefaultTableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int column) {
            Component result = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
            final TableEntry entry = (TableEntry) value;
            switch (column) {
            case COLUMN_THREAD_NAME:
                final String name = reader.getThreadNamesByID().get(entry.statistics.threadId);
                setText(name + " (" + entry.statistics.threadId + ")");
                break;
            case COLUMN_PERCENTAGE_BAR:
                return entry.percentageBar;
            default:
                int legendItem = column - 2;
                LegendItem item = UIConstants.LEGEND_ITEMS.get(legendItem);
                for (IPercentageProvider i : entry.percentageBar.getValues()) {
                    LegendItemProvider current = (LegendItemProvider) i;
                    if (current.item == item) {
                        setText(formatPercentageValue(current.percentageValue));
                        setHorizontalAlignment(JLabel.RIGHT);
                        break;
                    }
                }

            }
            return result;
        }
    }

    private static String formatPercentageValue(double value) {
        return new DecimalFormat("##0.##").format(value);
    }

    public StatisticsPanel(FileReader reader) {
        this.reader = reader;
        table.setDefaultRenderer(TableEntry.class, new CellRenderer());
        table.addMouseMotionListener(mouseListener);
        table.setBackground(Color.WHITE);
        table.setFillsViewportHeight(true);
        setLayout(new GridBagLayout());
        GridBagConstraints cnstrs = new GridBagConstraints();
        cnstrs.gridheight = GridBagConstraints.REMAINDER;
        cnstrs.gridwidth = GridBagConstraints.REMAINDER;
        cnstrs.fill = GridBagConstraints.BOTH;
        cnstrs.weightx = 1.0;
        cnstrs.weighty = 1.0;
        JScrollPane pane = new JScrollPane(table);
        pane.setBackground(Color.WHITE);
        add(pane, cnstrs);
    }

    public void setInterval(HiResInterval interval) throws IOException {
        this.interval = interval;
        recalcStatistics();
        repaint();
    }

    private void recalcStatistics() throws IOException {

        tableData.clear();

        if (interval == null) {
            return;
        }

        final Map<Integer, PerThreadStatistics> statistics = new HashMap<>();

        final Set<Integer> threadIds = reader.getAliveThreadsInInterval(interval);
        for (int threadId : threadIds) {
            statistics.put(threadId, new PerThreadStatistics(threadId));
        }

        final FileVisitor statisticsVisitor = new FileVisitor() {

            @Override
            public void visit(ThreadEvent event) {
                final PerThreadStatistics stat = statistics.get(event.threadId);
                stat.processEvent(event);
            }
        };
        reader.visit(statisticsVisitor, interval, threadIds);

        final double totalIntervalDurationMillis = interval.getDurationInMilliseconds();
        for (PerThreadStatistics s : statistics.values()) {
            s.finish(interval);

            if (s.containsData) {
                final PercentageBar bar = new PercentageBar();

                for (Entry<LegendItem, Double> v : s.sumDurationInMillis.entrySet()) {
                    double percentage = 100.0 * (v.getValue() / totalIntervalDurationMillis);
                    LegendItemProvider provider = new LegendItemProvider(v.getKey(), percentage);
                    bar.setPercentage(v.getKey().color, provider);
                }
                tableData.add(new TableEntry(s, bar));
            }
        }
        tableModel.fireDataChanged();
    }

    protected static final class LegendItemProvider implements IPercentageProvider {

        public final LegendItem item;
        public final double percentageValue;

        private LegendItemProvider(LegendItem item, double percentageValue) {
            this.item = item;
            this.percentageValue = percentageValue;
        }

        @Override
        public double getPercentageValue() {
            return percentageValue;
        }
    }
}