org.sonar.ide.eclipse.internal.ui.views.MeasuresView.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.ide.eclipse.internal.ui.views.MeasuresView.java

Source

/*
 * Sonar, open source software quality management tool.
 * Copyright (C) 2010-2012 SonarSource
 * mailto:contact AT sonarsource DOT com
 *
 * Sonar is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * Sonar is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sonar; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonar.ide.eclipse.internal.ui.views;

import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.actions.BaseSelectionListenerAction;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.ide.api.IMeasure;
import org.sonar.ide.api.SourceCode;
import org.sonar.ide.eclipse.core.ISonarMeasure;
import org.sonar.ide.eclipse.core.ISonarMetric;
import org.sonar.ide.eclipse.core.ISonarResource;
import org.sonar.ide.eclipse.core.SonarCorePlugin;
import org.sonar.ide.eclipse.internal.EclipseSonar;
import org.sonar.ide.eclipse.internal.core.ISonarConstants;
import org.sonar.ide.eclipse.internal.core.resources.ProjectProperties;
import org.sonar.ide.eclipse.internal.ui.AbstractTableLabelProvider;
import org.sonar.ide.eclipse.internal.ui.FavouriteMetricsManager;
import org.sonar.ide.eclipse.internal.ui.SonarImages;
import org.sonar.ide.eclipse.internal.ui.actions.ToggleFavouriteMetricAction;
import org.sonar.ide.eclipse.internal.ui.jobs.AbstractRemoteSonarJob;
import org.sonar.ide.eclipse.ui.SonarUiPlugin;
import org.sonar.wsclient.services.Measure;
import org.sonar.wsclient.services.Metric;
import org.sonar.wsclient.services.MetricQuery;
import org.sonar.wsclient.services.Resource;
import org.sonar.wsclient.services.ResourceQuery;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author Evgeny Mandrikov
 */
public class MeasuresView extends AbstractSonarInfoView {
    private static final Logger LOG = LoggerFactory.getLogger(MeasuresView.class);

    public static final String ID = ISonarConstants.PLUGIN_ID + ".views.MeasuresView";
    private static final String FAVORITES_CATEGORY = "Favourites";

    private TreeViewer viewer;
    private Map<String, ISonarMeasure> measuresByKey;
    private Map<String, Collection<ISonarMeasure>> measuresByDomain;

    private final BaseSelectionListenerAction toggleFavoriteAction = new ToggleFavouriteMetricAction();

    private final FavouriteMetricsManager.Listener favouriteMetricsListener = new FavouriteMetricsManager.Listener() {
        public void updated() {
            if (measuresByDomain == null) {
                return; // Avoid error but don't know why it happens
            }
            Collection<ISonarMeasure> favourites = measuresByDomain.get(FAVORITES_CATEGORY);
            if (favourites == null) {
                favourites = Lists.newArrayList();
                measuresByDomain.put(FAVORITES_CATEGORY, favourites);
            }

            favourites.clear();
            for (ISonarMetric metric : SonarUiPlugin.getFavouriteMetricsManager().get()) {
                ISonarMeasure measure = measuresByKey.get(metric.getKey());
                if (measure != null) {
                    favourites.add(measure);
                }
            }

            if (favourites.isEmpty()) {
                measuresByDomain.remove(FAVORITES_CATEGORY);
            }
            viewer.refresh();
        }
    };

    @Override
    protected void internalCreatePartControl(Composite parent) {
        PatternFilter filter = new PatternFilter() {
            /**
             * This is a workaround to show measures, which belongs to specified category.
             */
            @Override
            protected boolean isParentMatch(Viewer viewer, Object element) {
                Map<String, List<IMeasure>> map = (Map<String, List<IMeasure>>) viewer.getInput();
                if (element instanceof IMeasure) {
                    IMeasure measure = (IMeasure) element;
                    String domain = measure.getMetricDef().getDomain();
                    for (Map.Entry<String, List<IMeasure>> e : map.entrySet()) {
                        if (domain.equals(e.getKey())) {
                            return isLeafMatch(viewer, e);
                        }
                    }
                }
                return super.isParentMatch(viewer, element);
            }
        };
        FilteredTree filteredTree = new FilteredTree(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL, filter,
                true);
        viewer = filteredTree.getViewer();
        viewer.setContentProvider(new MapContentProvider());
        viewer.setLabelProvider(new MeasuresLabelProvider());
        viewer.setComparator(new ViewerComparator() {
            @SuppressWarnings("rawtypes")
            @Override
            public int category(Object element) {
                if (element instanceof Map.Entry) {
                    String s = (String) ((Map.Entry) element).getKey();
                    if (FAVORITES_CATEGORY.equals(s)) {
                        return 0;
                    } else {
                        return 1;
                    }
                }
                return super.category(element);
            }
        });
        Tree tree = viewer.getTree();
        tree.setHeaderVisible(true);
        tree.setLinesVisible(true);
        TreeColumn column1 = new TreeColumn(tree, SWT.LEFT);
        column1.setText("Name");
        column1.setWidth(200);
        TreeColumn column2 = new TreeColumn(tree, SWT.LEFT);
        column2.setText("Value");
        column2.setWidth(100);

        viewer.addSelectionChangedListener(toggleFavoriteAction);

        hookContextMenu();
        SonarUiPlugin.getFavouriteMetricsManager().addListener(favouriteMetricsListener);
    }

    private void hookContextMenu() {
        // Create menu manager
        MenuManager menuMgr = new MenuManager("#PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager mgr) {
                fillContextMenu(mgr);
            }
        });
        // Create menu
        Menu menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        // Register menu for extension
        getSite().registerContextMenu(menuMgr, viewer);
    }

    private void fillContextMenu(IMenuManager mgr) {
        // populate menu
        mgr.add(toggleFavoriteAction);
        // required, for extensions
        mgr.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    @Override
    protected Control getControl() {
        return viewer.getControl();
    }

    class MeasuresLabelProvider extends AbstractTableLabelProvider implements ILabelProvider {

        @Override
        public Image getColumnImage(Object element, int columnIndex) {
            if ((columnIndex == 1) && (element instanceof ISonarMeasure)) {
                ISonarMeasure measure = (ISonarMeasure) element;
                ImageDescriptor imageDescriptor = SonarImages.forTendency(measure);
                if (imageDescriptor != null) {
                    return imageDescriptor.createImage();
                }
            }
            return null;
        }

        @Override
        @SuppressWarnings("rawtypes")
        public String getColumnText(Object element, int columnIndex) {
            if (element instanceof Map.Entry) {
                if (columnIndex > 0) {
                    return "";
                }
                return ((Map.Entry) element).getKey().toString();
            }
            if (element instanceof ISonarMeasure) {
                ISonarMeasure measure = (ISonarMeasure) element;
                switch (columnIndex) {
                case 0:
                    return measure.getMetricDef().getName();
                case 1:
                    return measure.getValue();
                default:
                    return "";
                }
            }
            return "";
        }

        public Image getImage(Object element) {
            return null;
        }

        public String getText(Object element) {
            return getColumnText(element, 0);
        }
    }

    private void update(final Object content) {
        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                doAsyncUpdate(content);
            }
        });
    }

    protected void doAsyncUpdate(Object content) {
        LOG.debug("Update Measures view");

        try {
            setContentDescription(getInput().getName());
            viewer.setInput(content);
            viewer.expandAll();
        } catch (IllegalStateException e) {
            // Widget was closed
        }
    }

    @Override
    protected void doSetInput(Object input) {
        final ISonarResource element = (ISonarResource) input;
        Job job = new AbstractRemoteSonarJob("Loading measures") {
            @Override
            protected IStatus run(IProgressMonitor monitor) {
                monitor.beginTask("Loading measures for " + element.getKey(), IProgressMonitor.UNKNOWN);
                update(null);

                Collection<ISonarMeasure> measures = null;

                if (ProjectProperties.getInstance(element.getProject()).isAnalysedLocally()) {
                    // Local mode
                    try {
                        measures = (Collection<ISonarMeasure>) element.getResource()
                                .getSessionProperty(SonarCorePlugin.SESSION_PROPERY_MEASURES);
                    } catch (CoreException e) {
                        // ignore
                    }
                } else {
                    // Remote mode
                    EclipseSonar index = EclipseSonar.getInstance(element.getProject());
                    final SourceCode sourceCode = index.search(element);
                    if (sourceCode != null) {
                        measures = getMeasures(index, element);
                    }
                }

                if (measures == null) {
                    measures = Collections.emptyList();
                }

                final List<ISonarMeasure> favorites = Lists.newArrayList();

                // Group by key and domain
                final Map<String, ISonarMeasure> measuresByKey = Maps.newHashMap();
                final Multimap<String, ISonarMeasure> measuresByDomain = ArrayListMultimap.create();
                for (ISonarMeasure measure : measures) {
                    if (SonarUiPlugin.getFavouriteMetricsManager().isFavorite(measure.getMetricDef())) {
                        favorites.add(measure);
                    }
                    String domain = measure.getMetricDef().getDomain();
                    measuresByDomain.put(domain, measure);
                    measuresByKey.put(measure.getMetricDef().getKey(), measure);
                }

                MeasuresView.this.measuresByDomain = Maps.newHashMap(measuresByDomain.asMap());
                MeasuresView.this.measuresByKey = measuresByKey;
                if (!favorites.isEmpty()) {
                    MeasuresView.this.measuresByDomain.put(FAVORITES_CATEGORY, favorites);
                }
                update(MeasuresView.this.measuresByDomain);

                monitor.done();
                return Status.OK_STATUS;
            }
        };
        IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService) getSite()
                .getAdapter(IWorkbenchSiteProgressService.class);
        siteService.schedule(job);
    }

    public List<ISonarMeasure> getMeasures(EclipseSonar index, ISonarResource sonarResource) {
        List<ISonarMeasure> result = Lists.newArrayList();
        Map<String, Metric> metricsByKey = getMetrics(index);
        List<String> keys = Lists.newArrayList(metricsByKey.keySet());
        /*
         * Workaround for http://jira.codehaus.org/browse/SONAR-2430
         * Split list of metrics into small chunks and load measures by performing several queries.
         */
        for (List<String> keysForRequest : Lists.partition(keys, 5)) {
            String[] metricKeys = keysForRequest.toArray(new String[keysForRequest.size()]);
            ResourceQuery query = ResourceQuery.createForMetrics(sonarResource.getKey(), metricKeys)
                    .setIncludeTrends(true);
            Resource resource = index.getSonar().find(query);

            for (Measure measure : resource.getMeasures()) {
                final Metric metric = metricsByKey.get(measure.getMetricKey());
                // Hacks around SONAR-1620
                if (!metric.getHidden() && !"DATA".equals(metric.getType())
                        && StringUtils.isNotBlank(measure.getFormattedValue())) {
                    result.add(SonarCorePlugin.createSonarMeasure(sonarResource, metric, measure));
                }
            }
        }
        return result;
    }

    public Map<String, Metric> getMetrics(EclipseSonar index) {
        // TODO Godin: This is not optimal. Would be better to load metrics only once.
        List<Metric> metrics = index.getSonar().findAll(MetricQuery.all());
        return Maps.uniqueIndex(metrics, new Function<Metric, String>() {
            public String apply(Metric metric) {
                return metric.getKey();
            }
        });
    }

    @Override
    public void dispose() {
        SonarUiPlugin.getFavouriteMetricsManager().removeListener(favouriteMetricsListener);
        super.dispose();
    }
}