Java tutorial
/* * 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(); } }