net.sourceforge.metrics.ui.MetricsTable.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.metrics.ui.MetricsTable.java

Source

/*
 * Copyright (c) 2003 Frank Sauer. All rights reserved.
 *
 * Licenced under CPL 1.0 (Common Public License Version 1.0).
 * The licence is available at http://www.eclipse.org/legal/cpl-v10.html.
 *
 *
 * DISCLAIMER OF WARRANTIES AND LIABILITY:
 *
 * THE SOFTWARE IS PROVIDED "AS IS".  THE AUTHOR MAKES  NO REPRESENTATIONS OR WARRANTIES,
 * EITHER EXPRESS OR IMPLIED.  TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL THE
 * AUTHOR  BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE,  PROFITS
 * OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL  OR PUNITIVE DAMAGES,
 * HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF  LIABILITY, ARISING OUT OF OR RELATED TO
 * ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SOFTWARE, EVEN IF THE AUTHOR
 * HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 *
 * $Id: MetricsTable.java,v 1.39 2005/02/28 03:07:53 sauerf Exp $
 */
package net.sourceforge.metrics.ui;

import java.text.NumberFormat;
import java.util.Arrays;
import java.util.Comparator;

import net.sourceforge.metrics.core.Avg;
import net.sourceforge.metrics.core.Constants;
import net.sourceforge.metrics.core.Log;
import net.sourceforge.metrics.core.Max;
import net.sourceforge.metrics.core.Metric;
import net.sourceforge.metrics.core.MetricDescriptor;
import net.sourceforge.metrics.core.MetricsPlugin;
import net.sourceforge.metrics.core.sources.AbstractMetricSource;

import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.PartInitException;

/**
 * TableTree specialized for metrics display. Specializations include lazy child creation and child sorting in descending metric value
 * 
 * @author Frank Sauer
 */
public class MetricsTable extends Tree implements Constants, SelectionListener, TreeListener {

    private Color lastDefaultColor;
    private Color lastInRangeColor;
    private Color lastOutofRangeColor;
    private TreeColumn description;
    private TreeColumn value;
    private TreeColumn average;
    private TreeColumn variance;
    private TreeColumn max;
    private TreeColumn path;
    private TreeColumn method;

    /**
     * Constructor for MetricsTable.
     * 
     * @param parent
     * @param style
     */
    public MetricsTable(Composite parent, int style) {
        super(parent, style);
        setLinesVisible(true);
        setHeaderVisible(true);
        description = new TreeColumn(this, SWT.LEFT);
        description.setText("Metric");
        value = new TreeColumn(this, SWT.RIGHT);
        value.setText("Total");
        average = new TreeColumn(this, SWT.RIGHT);
        average.setText("Mean");
        variance = new TreeColumn(this, SWT.RIGHT);
        variance.setText("Std. Dev.");
        max = new TreeColumn(this, SWT.RIGHT);
        max.setText("Maximum");
        path = new TreeColumn(this, SWT.LEFT);
        path.setText("Resource causing Maximum");
        method = new TreeColumn(this, SWT.LEFT);
        method.setText("Method");
        addSelectionListener(this);
        addTreeListener(this);
    }

    /**
     * Update the table with new metrics.
     * 
     * @param ms
     */
    public void setMetrics(final AbstractMetricSource ms) {
        try {
            removeAll();
            if (ms == null) {
                return;
            }
            MetricsPlugin plugin = MetricsPlugin.getDefault();
            String[] names = plugin.getMetricIds();
            String[] descriptions = plugin.getMetricDescriptions();
            for (int i = 0; i < names.length; i++) {
                boolean rowNeeded = false;
                String name = names[i];
                String[] cols = new String[] { descriptions[i], "", "", "", "", "", "" };
                Metric m = ms.getValue(name);
                if (m != null) {
                    rowNeeded = true;
                    cols[1] = format(m.doubleValue());
                }
                for (String per : PER_ARRAY) {
                    Avg avg = ms.getAverage(name, per);
                    Max max = ms.getMaximum(name, per);
                    if ((avg != null) || (max != null)) {
                        TreeItem row = createNewRow();
                        cols[0] = descriptions[i] + " (avg/max per " + per + ")";
                        if (avg != null) {
                            cols[2] = format(avg.doubleValue());
                            cols[3] = format(avg.getStandardDeviation());
                        }
                        if (max != null) {
                            cols[4] = format(max.doubleValue());
                            String handle = max.getHandle();
                            if (handle != null) {
                                IJavaElement element = JavaCore.create(handle);
                                cols[5] = getPath(element);
                                setForeground(max, row);
                                cols[6] = getMethodName(element);
                                row.setData("handle", handle);
                                row.setData("element", element);
                            }
                        } else {
                            setForeground(m, row);
                        }
                        setText(row, cols);
                        addChildren(row, ms, name, per);
                        rowNeeded = false;
                    }
                }
                if (rowNeeded) {
                    TreeItem row = createNewRow();
                    setForeground(m, row);
                    setText(row, cols);
                    addChildren(row, ms, name, "");
                }
            }
        } catch (Throwable e) {
            Log.logError("MetricsTable::setMetrics", e);
        }
    }

    private void setForeground(Metric metric, TreeItem row) {
        if (metric == null) {
            row.setForeground(getDefaultForeground());
        } else {
            MetricDescriptor md = MetricsPlugin.getDefault().getMetricDescriptor(metric.getName());
            Color c = md.isValueInRange(metric.doubleValue()) ? getInRangeForeground() : getOutOfRangeForeground();
            row.setForeground(c);
        }
    }

    /**
     * create a new root row
     * 
     * @return
     */
    private TreeItem createNewRow() {
        TreeItem item = new TreeItem(this, SWT.NONE);
        item.setForeground(getDefaultForeground());
        return item;
    }

    /**
     * create a new child row
     * 
     * @param parent
     * @return
     */
    private TreeItem createNewRow(TreeItem parent) {
        TreeItem item = new TreeItem(parent, SWT.NONE);
        item.setForeground(getDefaultForeground());
        return item;
    }

    /**
     * Get the default color (for metrics without a max and within range)
     * 
     * @return
     */
    private Color getDefaultForeground() {
        RGB color = PreferenceConverter.getColor(MetricsPlugin.getDefault().getPreferenceStore(),
                "METRICS.defaultColor");
        if (lastDefaultColor == null) {
            lastDefaultColor = new Color(getDisplay(), color);
        } else if (!lastDefaultColor.getRGB().equals(color)) {
            lastDefaultColor.dispose();
            lastDefaultColor = new Color(getDisplay(), color);
        }
        return lastDefaultColor;
    }

    /**
     * Get the in range color (for metrics with a max and within range)
     * 
     * @return
     */
    private Color getInRangeForeground() {
        RGB color = PreferenceConverter.getColor(MetricsPlugin.getDefault().getPreferenceStore(),
                "METRICS.linkedColor");
        if (lastInRangeColor == null) {
            lastInRangeColor = new Color(getDisplay(), color);
        } else if (!lastInRangeColor.getRGB().equals(color)) {
            lastInRangeColor.dispose();
            lastInRangeColor = new Color(getDisplay(), color);
        }
        return lastInRangeColor;
    }

    /**
     * Get the out of range color (for metrics with a max and out of range)
     * 
     * @return
     */
    private Color getOutOfRangeForeground() {
        RGB color = PreferenceConverter.getColor(MetricsPlugin.getDefault().getPreferenceStore(),
                "METRICS.outOfRangeColor");
        if (lastOutofRangeColor == null) {
            lastOutofRangeColor = new Color(getDisplay(), color);
        } else if (!lastOutofRangeColor.getRGB().equals(color)) {
            lastOutofRangeColor.dispose();
            lastOutofRangeColor = new Color(getDisplay(), color);
        }
        return lastOutofRangeColor;
    }

    /**
     * first time simply adds a dummy child if there should be any children. When such a dummy child is already rpesent, replaces it with the real children. This spreads the work load across expansion events instead of constructing the
     * entire tree up front, which could be very time consuming in large projects.
     * 
     * @param row
     *            parent TreeItem
     * @param ms
     *            AbstractMetricSource containing the metrics
     * @param metric
     *            Name of the Metric
     * @param per
     *            name of the avg/max type
     */
    private void addChildren(TreeItem row, AbstractMetricSource ms, String metric, String per) {
        AbstractMetricSource[] children = ms.getChildrenHaving(per, metric);
        // don't have to do anything if there are no child metrics
        if ((children != null) && (children.length > 0)) {
            if (row.getData("source") != null) {
                // dummy already present, replace it with the real thing
                row.setData("source", null);
                row.getItems()[0].dispose();
                sort(children, metric, per);
                for (AbstractMetricSource element : children) {
                    TreeItem child = createNewRow(row);
                    child.setText(getElementName(element.getJavaElement()));
                    Metric val = element.getValue(metric);
                    child.setText(1, (val != null) ? format(val.doubleValue()) : "");
                    Avg avg = element.getAverage(metric, per);
                    child.setText(2, (avg != null) ? format(avg.doubleValue()) : "");
                    child.setText(3, (avg != null) ? format(avg.getStandardDeviation()) : "");
                    Max max = element.getMaximum(metric, per);
                    child.setText(4, (max != null) ? format(max.doubleValue()) : "");
                    if (max != null) {
                        IJavaElement maxElm = JavaCore.create(max.getHandle());
                        child.setText(5, getPath(maxElm));
                        child.setText(6, getMethodName(maxElm));
                        setForeground(max, child);
                    } else {
                        child.setText(5, "");
                        child.setText(6, "");
                        if (val != null) {
                            setForeground(val, row);
                        }
                    }
                    child.setData("handle", element.getHandle());
                    child.setData("element", element.getJavaElement());
                    // recurse
                    addChildren(child, element, metric, per);
                }
            } else { // add dummy
                createNewRow(row);
                row.setData("metric", metric);
                row.setData("per", per);
                row.setData("source", ms);
            }
        } else {
            Max max = ms.getMaximum(metric, per);
            if (max != null) {
                setForeground(max, row);
            } else {
                setForeground(ms.getValue(metric), row);
            }
        }
    }

    private String getElementName(IJavaElement element) {
        String candidate = element.getElementName();
        if ("".equals(candidate)) {
            if (element instanceof IType) {
                return "anonymous";
            }
            return "(default package)";
        }
        return candidate;
    }

    /**
     * Sort the metrics in descending max/value order, giving preference to max over value (if max exists, use it, otherwise use value)
     * 
     * @param children
     * @param metric
     * @param per
     * @return
     */
    private void sort(AbstractMetricSource[] children, final String metric, final String per) {
        Comparator<AbstractMetricSource> c = new Comparator<AbstractMetricSource>() {

            public int compare(AbstractMetricSource o1, AbstractMetricSource o2) {
                Max max1 = o1.getMaximum(metric, per);
                Max max2 = o2.getMaximum(metric, per);
                if ((max1 != null) && (max2 != null)) {
                    return -max1.compareTo(max2);
                } /* else { */
                Metric m1 = o1.getValue(metric);
                Metric m2 = o2.getValue(metric);
                if ((m1 != null) && (m2 != null)) {
                    return -m1.compareTo(m2);
                } /* else { */
                if ((max1 != null) && (max2 == null)) {
                    return -1;
                }
                if ((m1 != null) && (m2 == null)) {
                    return -1; // change from if ((m2 != null)&&(m2 ==
                    // null)) return -1;
                }
                return 1;
                /* } */
                /* } */
            }
        };
        Arrays.sort(children, c);
    }

    private void setText(TreeItem row, String[] columns) {
        row.setText(columns[0]);
        for (int i = 1; i < columns.length; i++) {
            row.setText(i, columns[i]);
        }
    }

    /**
     * @param handle
     * @return String
     */
    private String getMethodName(IJavaElement element) {
        return (element.getElementType() == IJavaElement.METHOD) ? element.getElementName() : "";
    }

    /**
     * @param handle
     * @return String
     */
    private String getPath(IJavaElement element) {
        return element.getPath().toString();
    }

    private String format(double value) {
        NumberFormat nf = NumberFormat.getInstance();
        int decimals = MetricsPlugin.getDefault().getPreferenceStore().getInt("METRICS.decimals");
        nf.setMaximumFractionDigits(decimals);
        nf.setGroupingUsed(false);
        return nf.format(value);
    }

    /**
     * @see org.eclipse.swt.widgets.Widget#checkSubclass()
     */
    @Override
    protected void checkSubclass() {
    }

    public void widgetSelected(SelectionEvent e) {
    }

    /**
     * react to double-clicks in the table by opening an editor on the resource for the metric
     * 
     * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent)
     */
    public void widgetDefaultSelected(SelectionEvent e) {
        TreeItem row = (TreeItem) e.item;
        IJavaElement element = (IJavaElement) row.getData("element");
        String handle = (String) row.getData("handle");
        try {
            if (element != null) {
                IEditorPart javaEditor = JavaUI.openInEditor(element);
                if (element instanceof IMember) {
                    JavaUI.revealInEditor(javaEditor, element);
                }
            }
        } catch (PartInitException x) {
            Log.logError("Error selecting " + handle, x);
            x.printStackTrace();
        } catch (JavaModelException x) {
            Log.logError("Error selecting " + handle, x);
            x.printStackTrace();
        } catch (Throwable t) {
            Log.logError("Error selecting " + handle, t);
            t.printStackTrace();
        }
    }

    private int getWidth(IMemento m, String name, int defaultVal) {
        try {
            Integer val = m.getInteger(name);
            return (val == null) ? defaultVal : val.intValue();
        } catch (Throwable e) {
            return defaultVal;
        }
    }

    void initWidths(IMemento memento) {
        description.setWidth(getWidth(memento, "description", 270));
        value.setWidth(getWidth(memento, "value", 50));
        average.setWidth(getWidth(memento, "average", 50));
        variance.setWidth(getWidth(memento, "variance", 60));
        max.setWidth(getWidth(memento, "max", 60));
        path.setWidth(getWidth(memento, "path", 300));
        method.setWidth(getWidth(memento, "method", 150));
    }

    void updateWidths(IMemento memento) {
        memento.putInteger("description", description.getWidth());
        memento.putInteger("value", value.getWidth());
        memento.putInteger("average", average.getWidth());
        memento.putInteger("variance", variance.getWidth());
        memento.putInteger("max", max.getWidth());
        memento.putInteger("path", path.getWidth());
        memento.putInteger("method", method.getWidth());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.swt.events.TreeListener#treeCollapsed(org.eclipse.swt.events .TreeEvent)
     */
    public void treeCollapsed(TreeEvent e) {
    }

    /**
     * replaces dummy child nodes with the real children if needed
     */
    public void treeExpanded(TreeEvent e) {
        TreeItem item = (TreeItem) e.item;
        if (item.getData("source") != null) {
            AbstractMetricSource source = (AbstractMetricSource) item.getData("source");
            String metric = (String) item.getData("metric");
            String per = (String) item.getData("per");
            addChildren(item, source, metric, per);
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.swt.widgets.Widget#dispose()
     */
    @Override
    public void dispose() {
        super.dispose();
        if (lastDefaultColor != null) {
            lastDefaultColor.dispose();
        }
        if (lastInRangeColor != null) {
            lastInRangeColor.dispose();
        }
        if (lastOutofRangeColor != null) {
            lastOutofRangeColor.dispose();
        }
    }

}