org.eclipse.linuxtools.internal.valgrind.massif.MassifViewPart.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.linuxtools.internal.valgrind.massif.MassifViewPart.java

Source

/*******************************************************************************
 * Copyright (c) 2008 Red Hat, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Elliott Baron <ebaron@redhat.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.valgrind.massif;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.linuxtools.internal.valgrind.massif.MassifSnapshot.SnapshotType;
import org.eclipse.linuxtools.internal.valgrind.massif.birt.ChartEditorInput;
import org.eclipse.linuxtools.internal.valgrind.massif.birt.ChartSVG;
import org.eclipse.linuxtools.internal.valgrind.massif.birt.HeapChart;
import org.eclipse.linuxtools.valgrind.ui.IValgrindToolView;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

public class MassifViewPart extends ViewPart implements IValgrindToolView {

    protected static final String TITLE_STACKS = Messages.getString("MassifViewPart.Stacks"); //$NON-NLS-1$
    protected static final String TITLE_EXTRA = Messages.getString("MassifViewPart.Extra_Heap"); //$NON-NLS-1$
    protected static final String TITLE_USEFUL = Messages.getString("MassifViewPart.Useful_Heap"); //$NON-NLS-1$
    protected static final String TITLE_TOTAL = Messages.getString("MassifViewPart.Total"); //$NON-NLS-1$
    protected static final String TITLE_TIME = Messages.getString("MassifViewPart.Time"); //$NON-NLS-1$
    protected static final String TITLE_NUMBER = Messages.getString("MassifViewPart.Snapshot"); //$NON-NLS-1$
    protected static final String TREE_ACTION = MassifPlugin.PLUGIN_ID + ".treeAction"; //$NON-NLS-1$
    public static final String CHART_ACTION = MassifPlugin.PLUGIN_ID + ".chartAction"; //$NON-NLS-1$
    public static final String PID_ACTION = MassifPlugin.PLUGIN_ID + ".pidAction"; //$NON-NLS-1$
    public static final String SAVE_CHART_ACTION = MassifPlugin.PLUGIN_ID + ".saveChartAction"; //$NON-NLS-1$

    protected MassifOutput output;
    protected Integer pid;

    protected Composite top;
    protected StackLayout stackLayout;
    protected TableViewer viewer;
    protected MassifTreeViewer treeViewer;
    protected MassifHeapTreeNode[] nodes;
    protected String chartName;

    protected static final int COLUMN_SIZE = 125;

    protected Action treeAction;
    protected Action chartAction;
    protected MassifPidMenuAction pidAction;
    protected Action saveChartAction;

    protected List<ChartEditorInput> chartInputs;

    @Override
    public void createPartControl(Composite parent) {
        chartInputs = new ArrayList<ChartEditorInput>();

        top = new Composite(parent, SWT.NONE);
        stackLayout = new StackLayout();
        top.setLayout(stackLayout);
        top.setLayoutData(new GridData(GridData.FILL_BOTH));

        viewer = new TableViewer(top, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);

        Table table = viewer.getTable();
        table.setLayoutData(new GridData(GridData.FILL_BOTH));

        String[] columnTitles = { TITLE_NUMBER, TITLE_TIME, TITLE_TOTAL, TITLE_USEFUL, TITLE_EXTRA, TITLE_STACKS };

        for (int i = 0; i < columnTitles.length; i++) {
            TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE);
            column.getColumn().setText(columnTitles[i]);
            column.getColumn().setWidth(COLUMN_SIZE);
            column.getColumn().setResizable(true);
            column.getColumn().addSelectionListener(getHeaderListener());
        }
        table.setHeaderVisible(true);
        table.setLinesVisible(true);

        viewer.setContentProvider(new ArrayContentProvider());
        viewer.setLabelProvider(new MassifLabelProvider());

        treeViewer = new MassifTreeViewer(top);
        treeViewer.getViewer().getControl().setLayoutData(new GridData(GridData.FILL_BOTH));

        viewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                MassifSnapshot snapshot = (MassifSnapshot) ((IStructuredSelection) event.getSelection())
                        .getFirstElement();
                if (snapshot.getType() != SnapshotType.EMPTY) {
                    treeAction.setChecked(true);
                    setTopControl(treeViewer.getViewer().getControl());
                    treeViewer.getViewer().setSelection(new StructuredSelection(snapshot.getRoot()), true);
                    treeViewer.getViewer().expandToLevel(snapshot.getRoot(), TreeViewer.ALL_LEVELS);
                }
            }
        });

        stackLayout.topControl = viewer.getControl();
        top.layout();
    }

    private String getUnitString(MassifSnapshot[] snapshots) {
        String result;
        MassifSnapshot snapshot = snapshots[0];
        switch (snapshot.getUnit()) {
        case BYTES:
            result = "B"; //$NON-NLS-1$
            break;
        case INSTRUCTIONS:
            result = "i"; //$NON-NLS-1$
            break;
        default:
            result = "ms"; //$NON-NLS-1$
            break;
        }
        return result;
    }

    private SelectionListener getHeaderListener() {
        return new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                TableColumn column = (TableColumn) e.widget;
                Table table = viewer.getTable();
                if (column.equals(table.getSortColumn())) {
                    int direction = table.getSortDirection() == SWT.UP ? SWT.DOWN : SWT.UP;
                    table.setSortDirection(direction);
                } else {
                    table.setSortDirection(SWT.UP);
                }
                table.setSortColumn(column);
                viewer.setComparator(new ViewerComparator() {
                    @Override
                    public int compare(Viewer viewer, Object o1, Object o2) {
                        Table table = ((TableViewer) viewer).getTable();
                        int direction = table.getSortDirection();
                        MassifSnapshot s1 = (MassifSnapshot) o1;
                        MassifSnapshot s2 = (MassifSnapshot) o2;
                        long result;
                        TableColumn column = table.getSortColumn();
                        if (column.getText().equals(TITLE_NUMBER)) {
                            result = s1.getNumber() - s2.getNumber();
                        } else if (column.getText().startsWith(TITLE_TIME)) {
                            result = s1.getTime() - s2.getTime();
                        } else if (column.getText().equals(TITLE_TOTAL)) {
                            result = s1.getTotal() - s2.getTotal();
                        } else if (column.getText().equals(TITLE_USEFUL)) {
                            result = s1.getHeapBytes() - s2.getHeapBytes();
                        } else if (column.getText().equals(TITLE_EXTRA)) {
                            result = s1.getHeapExtra() - s2.getHeapExtra();
                        } else {
                            result = s1.getStacks() - s2.getStacks();
                        }

                        // ascending / descending
                        result = direction == SWT.UP ? result : -result;

                        // overflow check
                        if (result > Integer.MAX_VALUE) {
                            result = Integer.MAX_VALUE;
                        } else if (result < Integer.MIN_VALUE) {
                            result = Integer.MIN_VALUE;
                        }
                        return (int) result;
                    }
                });
            }
        };
    }

    public IAction[] getToolbarActions() {
        pidAction = new MassifPidMenuAction(this);
        pidAction.setId(PID_ACTION);

        chartAction = new Action(Messages.getString("MassifViewPart.Display_Heap_Allocation"), //$NON-NLS-1$
                IAction.AS_PUSH_BUTTON) {
            @Override
            public void run() {
                ChartEditorInput input = getChartInput(pid);
                if (input != null) {
                    displayChart(input);
                }
            }
        };
        chartAction.setId(CHART_ACTION);
        chartAction.setImageDescriptor(
                MassifPlugin.imageDescriptorFromPlugin(MassifPlugin.PLUGIN_ID, "icons/linecharticon.gif")); //$NON-NLS-1$
        chartAction.setToolTipText(Messages.getString("MassifViewPart.Display_Heap_Allocation")); //$NON-NLS-1$

        saveChartAction = new Action(Messages.getString("MassifViewPart.Save_Chart"), IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
            @Override
            public void run() {
                ChartEditorInput currentInput = getChartInput(pid);
                String path = getChartSavePath(currentInput.getName() + ".svg"); //$NON-NLS-1$
                if (path != null) {
                    ChartSVG renderer = new ChartSVG(currentInput.getChart(), MassifViewPart.this);
                    renderer.renderSVG(Path.fromOSString(path));
                }
            }
        };
        saveChartAction.setId(SAVE_CHART_ACTION);
        saveChartAction.setImageDescriptor(
                MassifPlugin.imageDescriptorFromPlugin(MassifPlugin.PLUGIN_ID, "icons/chart-save.png")); //$NON-NLS-1$
        saveChartAction.setToolTipText(Messages.getString("MassifViewPart.Save_Chart")); //$NON-NLS-1$

        treeAction = new Action(Messages.getString("MassifViewPart.Show_Heap_Tree"), IAction.AS_CHECK_BOX) { //$NON-NLS-1$
            @Override
            public void run() {
                if (isChecked()) {
                    stackLayout.topControl = treeViewer.getViewer().getControl();
                    top.layout();
                } else {
                    stackLayout.topControl = viewer.getControl();
                    top.layout();
                }
            }
        };
        treeAction.setId(TREE_ACTION);
        treeAction.setImageDescriptor(
                MassifPlugin.imageDescriptorFromPlugin(MassifPlugin.PLUGIN_ID, "icons/call_hierarchy.gif")); //$NON-NLS-1$
        treeAction.setToolTipText(Messages.getString("MassifViewPart.Show_Heap_Tree")); //$NON-NLS-1$

        return new IAction[] { pidAction, chartAction, saveChartAction, treeAction };
    }

    protected String getChartSavePath(String defaultName) {
        Shell parent = new Shell(Display.getDefault());
        FileDialog dialog = new FileDialog(parent, SWT.SAVE);
        dialog.setText(Messages.getString("MassifViewPart.Save_chart_dialog_title")); //$NON-NLS-1$
        dialog.setOverwrite(true);
        dialog.setFilterExtensions(new String[] { ".svg" }); //$NON-NLS-1$
        dialog.setFileName(defaultName);

        return dialog.open();
    }

    protected void createChart(MassifSnapshot[] snapshots) {
        HeapChart chart = new HeapChart(snapshots);
        String title = chartName + " [PID: " + pid + "]"; //$NON-NLS-1$//$NON-NLS-2$
        chart.getTitle().getLabel().getCaption().setValue(title);

        String name = getInputName(title);
        ChartEditorInput input = new ChartEditorInput(chart, this, name, pid);
        chartInputs.add(input);

        // open the editor
        displayChart(input);
    }

    protected void displayChart(final ChartEditorInput chartInput) {
        Display.getDefault().syncExec(new Runnable() {
            public void run() {
                try {
                    IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
                    page.openEditor(chartInput, MassifPlugin.EDITOR_ID);
                } catch (PartInitException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    protected String getInputName(String description) {
        String launchName = description.substring(0, description.indexOf("[massif")); //$NON-NLS-1$
        return launchName.trim();
    }

    @Override
    public void setFocus() {
        viewer.getTable().setFocus();
    }

    public void refreshView() {
        if (output != null && pid != null) {
            MassifSnapshot[] snapshots = output.getSnapshots(pid);
            pidAction.setPids(output.getPids());
            if (snapshots != null) {
                viewer.setInput(snapshots);

                String timeWithUnit = TITLE_TIME + " (" + getUnitString(snapshots) + ")"; //$NON-NLS-1$ //$NON-NLS-2$
                for (TableColumn column : viewer.getTable().getColumns()) {
                    if (column.getText().startsWith(TITLE_TIME)) {
                        column.setText(timeWithUnit);
                    }
                    viewer.getTable().layout(true);
                }
                MassifSnapshot[] detailed = getDetailed(snapshots);
                nodes = new MassifHeapTreeNode[detailed.length];
                for (int i = 0; i < detailed.length; i++) {
                    nodes[i] = detailed[i].getRoot();
                }
                treeViewer.getViewer().setInput(nodes);

                // create and display chart
                if (snapshots.length > 0) {
                    ChartEditorInput input = getChartInput(pid);
                    if (input == null) {
                        createChart(snapshots);
                    } else {
                        displayChart(input);
                    }
                }
            }
        }
    }

    @Override
    public void dispose() {
        // Close all chart editors to keep Valgrind output consistent throughout workbench
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        if (page != null) {
            for (IEditorInput input : chartInputs) {
                IEditorPart part = page.findEditor(input);
                if (part != null) {
                    page.closeEditor(part, false);
                }
            }
        }
        super.dispose();
    }

    public void setTopControl(Control control) {
        stackLayout.topControl = control;
        top.layout(true);
    }

    public void setOutput(MassifOutput output) {
        this.output = output;
    }

    public MassifOutput getOutput() {
        return output;
    }

    public void setPid(Integer pid) {
        this.pid = pid;
    }

    public Integer getPid() {
        return pid;
    }

    public MassifSnapshot[] getSnapshots() {
        return output != null && pid != null ? output.getSnapshots(pid) : null;
    }

    public TableViewer getTableViewer() {
        return viewer;
    }

    public MassifTreeViewer getTreeViewer() {
        return treeViewer;
    }

    protected class MassifLabelProvider extends LabelProvider implements ITableLabelProvider, IFontProvider {

        public Image getColumnImage(Object element, int columnIndex) {
            Image image = null;
            if (columnIndex == 0) {
                MassifSnapshot snapshot = (MassifSnapshot) element;
                switch (snapshot.getType()) {
                case EMPTY:
                    break;
                case PEAK:
                case DETAILED:
                    image = MassifPlugin
                            .imageDescriptorFromPlugin(MassifPlugin.PLUGIN_ID, "icons/call_hierarchy.gif") //$NON-NLS-1$
                            .createImage();
                }
            }
            return image;
        }

        public String getColumnText(Object element, int columnIndex) {
            MassifSnapshot snapshot = (MassifSnapshot) element;
            DecimalFormat df = new DecimalFormat("#,##0"); //$NON-NLS-1$
            switch (columnIndex) {
            case 0:
                return df.format(snapshot.getNumber());
            case 1:
                return df.format(snapshot.getTime());
            case 2:
                return df.format(snapshot.getTotal());
            case 3:
                return df.format(snapshot.getHeapBytes());
            case 4:
                return df.format(snapshot.getHeapExtra());
            default:
                return df.format(snapshot.getStacks());
            }
        }

        public Font getFont(Object element) {
            Font font = null;
            MassifSnapshot snapshot = (MassifSnapshot) element;
            switch (snapshot.getType()) {
            case EMPTY:
            case DETAILED:
                break;
            case PEAK:
                font = JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
            }
            return font;
        }
    }

    public void setChartName(String chartName) {
        this.chartName = chartName;
    }

    private MassifSnapshot[] getDetailed(MassifSnapshot[] snapshots) {
        ArrayList<MassifSnapshot> list = new ArrayList<MassifSnapshot>();
        for (MassifSnapshot snapshot : snapshots) {
            if (snapshot.getType() != SnapshotType.EMPTY) {
                list.add(snapshot);
            }
        }
        return list.toArray(new MassifSnapshot[list.size()]);
    }

    private ChartEditorInput getChartInput(Integer pid) {
        ChartEditorInput result = null;
        for (int i = 0; i < chartInputs.size(); i++) {
            ChartEditorInput input = chartInputs.get(i);
            if (input.getPid().equals(pid)) {
                result = input;
            }
        }
        return result;
    }
}