Java tutorial
//============================================================================ // // Copyright (C) 2002-2006 David Schneider, Lars Kdderitzsch, Fabrice Bellingard // // This library 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 2.1 of the License, or (at your option) any later version. // // This library 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 this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //============================================================================ package net.sf.eclipsecs.ui.stats.views; import java.awt.Color; import java.awt.Font; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import net.sf.eclipsecs.core.util.CheckstyleLog; import net.sf.eclipsecs.ui.CheckstyleUIPluginImages; import net.sf.eclipsecs.ui.CheckstyleUIPluginPrefs; import net.sf.eclipsecs.ui.stats.Messages; import net.sf.eclipsecs.ui.stats.data.MarkerStat; import net.sf.eclipsecs.ui.stats.data.Stats; import net.sf.eclipsecs.ui.stats.views.internal.FiltersAction; import net.sf.eclipsecs.ui.util.table.EnhancedTableViewer; import net.sf.eclipsecs.ui.util.table.ITableComparableProvider; import net.sf.eclipsecs.ui.util.table.ITableSettingsProvider; import org.eclipse.core.resources.IMarker; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StackLayout; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.texteditor.MarkerUtilities; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.JFreeChart; import org.jfree.chart.entity.PieSectionEntity; import org.jfree.chart.plot.PiePlot; import org.jfree.experimental.chart.swt.ChartComposite; import org.jfree.ui.RectangleInsets; import org.jfree.util.Rotation; import org.osgi.service.prefs.BackingStoreException; /** * View that shows a graph for the Checkstyle marker distribution. * * @author Fabrice BELLINGARD * @author Lars Kdderitzsch */ public class GraphStatsView extends AbstractStatsView { // // constants // /** The unique view id. */ public static final String VIEW_ID = GraphStatsView.class.getName(); private static final String TAG_SECTION_DETAIL = "detailView"; // // attributes // /** The label containing the view description. */ private Label mLabelDesc; /** The main composite. */ private Composite mMainSection; /** The stack layout of the main composite. */ private StackLayout mStackLayout; /** The detail viewer. */ private EnhancedTableViewer mDetailViewer; /** The composite to harbor the JFreeChart control. */ private Composite mMasterComposite; /** The graph component. */ private JFreeChart mGraph; /** The dataset for the graph. */ private GraphPieDataset mPieDataset; /** The action to go back to the master view. */ private Action mDrillBackAction; /** Opens the editor and shows the error in the code. */ private Action mShowErrorAction; /** Action to go back to the marker overview view. */ private Action mListingAction; /** Allows to show all the categories or not. */ private Action mShowAllCategoriesAction; /** The current violation category to show in details view. */ private String mCurrentDetailCategory; /** The state if the view is currently drilled down to details. */ private boolean mIsDrilledDown; // // methods // /** * {@inheritDoc} * * @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ public void createPartControl(Composite parent) { super.createPartControl(parent); // set up the main layout GridLayout layout = new GridLayout(1, false); layout.marginWidth = 0; layout.marginHeight = 0; parent.setLayout(layout); // the label mLabelDesc = new Label(parent, SWT.NONE); GridData gridData = new GridData(); gridData.horizontalAlignment = GridData.FILL; gridData.grabExcessHorizontalSpace = true; mLabelDesc.setLayoutData(gridData); // the main section mMainSection = new Composite(parent, SWT.NONE); mStackLayout = new StackLayout(); mStackLayout.marginHeight = 0; mStackLayout.marginWidth = 0; mMainSection.setLayout(mStackLayout); mMainSection.setLayoutData(new GridData(GridData.FILL_BOTH)); // create the master viewer mMasterComposite = createMasterView(mMainSection); // create the detail viewer mDetailViewer = createDetailView(mMainSection); mStackLayout.topControl = mMasterComposite; updateActions(); // initialize the view data refresh(); } /** * Creates the master view containing the chart. * * @param parent * the parent composite * @return the chart composite */ public Composite createMasterView(Composite parent) { // create the date set for the chart mPieDataset = new GraphPieDataset(); mPieDataset.setShowAllCategories(mShowAllCategoriesAction.isChecked()); mGraph = createChart(mPieDataset); // creates the chart component // Composite embeddedComposite = new Composite(parent, SWT.EMBEDDED | SWT.NO_BACKGROUND); // embeddedComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); // experimental usage of JFreeChart SWT // the composite to harbor the Swing chart control ChartComposite embeddedComposite = new ChartComposite(parent, SWT.NONE, mGraph, true); embeddedComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); embeddedComposite.addChartMouseListener(new ChartMouseListener() { public void chartMouseClicked(final ChartMouseEvent event) { MouseEvent trigger = event.getTrigger(); if (trigger.getButton() == MouseEvent.BUTTON1 && event.getEntity() instanceof PieSectionEntity) { // && trigger.getClickCount() == 2 //doubleclick not correctly detected with the SWT composite mMasterComposite.getDisplay().syncExec(new Runnable() { public void run() { mIsDrilledDown = true; mCurrentDetailCategory = (String) ((PieSectionEntity) event.getEntity()) .getSectionKey(); mStackLayout.topControl = mDetailViewer.getTable(); mMainSection.layout(); mDetailViewer.setInput(mDetailViewer.getInput()); updateActions(); updateLabel(); } }); } else { event.getTrigger().consume(); } } public void chartMouseMoved(ChartMouseEvent event) { // NOOP } }); return embeddedComposite; } /** * Creates the table viewer for the detail view. * * @param parent * the parent composite * @return the detail table viewer */ private EnhancedTableViewer createDetailView(Composite parent) { // le tableau EnhancedTableViewer detailViewer = new EnhancedTableViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION); GridData gridData = new GridData(GridData.FILL_BOTH); detailViewer.getControl().setLayoutData(gridData); // setup the table columns Table table = detailViewer.getTable(); table.setLinesVisible(true); table.setHeaderVisible(true); TableColumn severityCol = new TableColumn(table, SWT.CENTER, 0); severityCol.setWidth(20); severityCol.setResizable(false); TableColumn idCol = new TableColumn(table, SWT.LEFT, 1); idCol.setText(Messages.MarkerStatsView_fileColumn); idCol.setWidth(150); TableColumn folderCol = new TableColumn(table, SWT.LEFT, 2); folderCol.setText(Messages.MarkerStatsView_folderColumn); folderCol.setWidth(300); TableColumn countCol = new TableColumn(table, SWT.CENTER, 3); countCol.setText(Messages.MarkerStatsView_lineColumn); countCol.pack(); TableColumn messageCol = new TableColumn(table, SWT.LEFT, 4); messageCol.setText(Messages.MarkerStatsView_messageColumn); messageCol.setWidth(300); // set the providers detailViewer.setContentProvider(new DetailContentProvider()); DetailViewMultiProvider multiProvider = new DetailViewMultiProvider(); detailViewer.setLabelProvider(multiProvider); detailViewer.setTableComparableProvider(multiProvider); detailViewer.setTableSettingsProvider(multiProvider); detailViewer.installEnhancements(); // add selection listener to maintain action state detailViewer.addSelectionChangedListener(new ISelectionChangedListener() { public void selectionChanged(SelectionChangedEvent event) { updateActions(); } }); // hooks the action to double click hookDoubleClickAction(mShowErrorAction, detailViewer); // and to the context menu too ArrayList actionList = new ArrayList(1); actionList.add(mDrillBackAction); actionList.add(mShowErrorAction); actionList.add(new Separator()); hookContextMenu(actionList, detailViewer); return detailViewer; } /** * See method below. * * @see net.sf.eclipsecs.stats.views.AbstractStatsView#makeActions() */ protected void makeActions() { mListingAction = new Action() { public void run() { try { getSite().getWorkbenchWindow().getActivePage().showView(MarkerStatsView.VIEW_ID); } catch (PartInitException e) { CheckstyleLog.log(e, NLS.bind(Messages.GraphStatsView_unableToOpenListingView, MarkerStatsView.VIEW_ID)); // TODO : mettre message d'erreur l'utilisateur } } }; mListingAction.setText(Messages.GraphStatsView_displayListing); mListingAction.setToolTipText(Messages.GraphStatsView_displayListing); mListingAction.setImageDescriptor(CheckstyleUIPluginImages.LIST_VIEW_ICON); mShowAllCategoriesAction = new Action(Messages.GraphStatsView_displayAllCategories, Action.AS_CHECK_BOX) { public void run() { Display.getDefault().asyncExec(new Runnable() { public void run() { if (!mMasterComposite.isDisposed() && mMasterComposite.isVisible()) { // on averti le dataset mPieDataset.setShowAllCategories(mShowAllCategoriesAction.isChecked()); // update the preference try { CheckstyleUIPluginPrefs.setBoolean( CheckstyleUIPluginPrefs.PREF_STATS_SHOW_ALL_CATEGORIES, mShowAllCategoriesAction.isChecked()); } catch (BackingStoreException e1) { CheckstyleLog.log(e1); } refresh(); } } }); } }; mShowAllCategoriesAction.setToolTipText(Messages.GraphStatsView_displayAllCategories); mShowAllCategoriesAction.setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_ELEMENT)); mShowAllCategoriesAction.setChecked( CheckstyleUIPluginPrefs.getBoolean(CheckstyleUIPluginPrefs.PREF_STATS_SHOW_ALL_CATEGORIES)); // action used to go back to the master view mDrillBackAction = new Action() { public void run() { drillBack(); } }; mDrillBackAction.setText(Messages.MarkerStatsView_actionBack); mDrillBackAction.setToolTipText(Messages.MarkerStatsView_actionBackTooltip); mDrillBackAction.setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_BACK)); mDrillBackAction.setDisabledImageDescriptor(PlatformUI.getWorkbench().getSharedImages() .getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED)); // action used to show a specific error in the editor mShowErrorAction = new Action() { public void run() { IStructuredSelection selection = (IStructuredSelection) mDetailViewer.getSelection(); if (selection.getFirstElement() instanceof IMarker) { IMarker marker = (IMarker) selection.getFirstElement(); try { IDE.openEditor(getSite().getPage(), marker); } catch (PartInitException e) { CheckstyleLog.log(e, Messages.MarkerStatsView_unableToShowMarker); // TODO : Open information dialog to notify the user } } } }; mShowErrorAction.setText(Messages.MarkerStatsView_displayError); mShowErrorAction.setToolTipText(Messages.MarkerStatsView_displayErrorTooltip); mShowErrorAction.setImageDescriptor( PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(IDE.SharedImages.IMG_OPEN_MARKER)); } /** * {@inheritDoc} */ protected void initMenu(IMenuManager menu) { menu.add(new FiltersAction(this)); menu.add(new Separator()); menu.add(mShowAllCategoriesAction); } /** * {@inheritDoc} */ protected void initToolBar(IToolBarManager tbm) { tbm.add(mListingAction); tbm.add(new Separator()); tbm.add(mDrillBackAction); // tbm.add(mShowAllCategoriesAction); tbm.add(new FiltersAction(this)); } /** * Helper method to manage the state of the view's actions. */ private void updateActions() { mDrillBackAction.setEnabled(mIsDrilledDown); mShowErrorAction.setEnabled(mIsDrilledDown && !mDetailViewer.getSelection().isEmpty()); } private void drillBack() { mIsDrilledDown = false; mCurrentDetailCategory = null; mStackLayout.topControl = mMasterComposite; mMainSection.layout(); updateActions(); updateLabel(); } /** * Specifies which action will be run when double clicking on the viewer. * * @param action * the IAction to add */ private void hookDoubleClickAction(final IAction action, StructuredViewer viewer) { viewer.addDoubleClickListener(new IDoubleClickListener() { public void doubleClick(DoubleClickEvent event) { action.run(); } }); } /** * Adds the actions to the tableviewer context menu. * * @param actions * a collection of IAction objets */ private void hookContextMenu(final Collection actions, StructuredViewer viewer) { MenuManager menuMgr = new MenuManager(); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { for (Iterator iter = actions.iterator(); iter.hasNext();) { Object item = iter.next(); if (item instanceof IContributionItem) { manager.add((IContributionItem) item); } else if (item instanceof IAction) { manager.add((IAction) item); } } manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); getSite().registerContextMenu(menuMgr, viewer); } /** * {@inheritDoc} */ protected String getViewId() { return VIEW_ID; } /** * {@inheritDoc} */ protected void handleStatsRebuilt() { if (!mMasterComposite.isDisposed()) { // change the marker stats on the data set mPieDataset.setStats(getStats()); mDetailViewer.setInput(getStats()); if (mIsDrilledDown && mDetailViewer.getTable().getItemCount() == 0) { drillBack(); } else { // update the actions and the label updateActions(); updateLabel(); } } } /** * Cre le graphe JFreeChart. * * @param piedataset * : la source de donnes afficher * @return le diagramme */ private JFreeChart createChart(GraphPieDataset piedataset) { JFreeChart jfreechart = ChartFactory.createPieChart3D(null, piedataset, false, true, false); jfreechart.setAntiAlias(true); jfreechart.setTextAntiAlias(true); PiePlot pieplot3d = (PiePlot) jfreechart.getPlot(); pieplot3d.setInsets(new RectangleInsets(0, 0, 0, 0)); final double angle = 290D; pieplot3d.setStartAngle(angle); pieplot3d.setDirection(Rotation.CLOCKWISE); final float foreground = 0.5F; pieplot3d.setForegroundAlpha(foreground); pieplot3d.setNoDataMessage(Messages.GraphStatsView_noDataToDisplay); pieplot3d.setCircular(true); pieplot3d.setOutlinePaint(null); pieplot3d.setLabelFont(new Font("SansSerif", Font.PLAIN, 10)); pieplot3d.setLabelGap(0.02); pieplot3d.setLabelOutlinePaint(null); pieplot3d.setLabelShadowPaint(null); pieplot3d.setLabelBackgroundPaint(Color.WHITE); pieplot3d.setBackgroundPaint(Color.WHITE); pieplot3d.setInteriorGap(0.02); pieplot3d.setMaximumLabelWidth(0.20); return jfreechart; } /** * Updates the title label. */ private void updateLabel() { if (!mIsDrilledDown) { Stats stats = getStats(); if (stats != null) { String text = NLS.bind(Messages.GraphStatsView_lblViewMessage, new Object[] { new Integer(stats.getMarkerCount()), new Integer(stats.getMarkerStats().size()), new Integer(stats.getMarkerCountAll()) }); mLabelDesc.setText(text); } else { mLabelDesc.setText(""); } } else { String text = NLS.bind(Messages.MarkerStatsView_lblDetailMessage, new Object[] { mCurrentDetailCategory, new Integer(mDetailViewer.getTable().getItemCount()) }); mLabelDesc.setText(text); } } /** * Content provider for the detail table viewer. * * @author Lars Kdderitzsch */ private class DetailContentProvider implements IStructuredContentProvider { private Object[] mCurrentDetails; /** * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) */ public Object[] getElements(Object inputElement) { if (mCurrentDetails == null) { // find the marker statistics for the current category Stats currentStats = (Stats) inputElement; Collection markerStats = currentStats.getMarkerStats(); Iterator it = markerStats.iterator(); while (it.hasNext()) { MarkerStat markerStat = (MarkerStat) it.next(); if (markerStat.getIdentifiant().equals(mCurrentDetailCategory)) { mCurrentDetails = markerStat.getMarkers().toArray(); break; } } } return mCurrentDetails != null ? mCurrentDetails : new Object[0]; } /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ public void dispose() { mCurrentDetails = null; } /** * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, * java.lang.Object, java.lang.Object) */ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { mCurrentDetails = null; } } /** * Label provider for the detail table viewer. * * @author Lars Kdderitzsch */ private class DetailViewMultiProvider extends LabelProvider implements ITableLabelProvider, ITableComparableProvider, ITableSettingsProvider { /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) */ public String getColumnText(Object obj, int index) { IMarker marker = (IMarker) obj; String text = null; try { switch (index) { case 1: text = marker.getResource().getName(); break; case 2: text = marker.getResource().getParent().getFullPath().toString(); break; case 3: text = marker.getAttribute(IMarker.LINE_NUMBER).toString(); break; case 4: text = marker.getAttribute(IMarker.MESSAGE).toString(); break; default: text = ""; //$NON-NLS-1$ break; } } catch (Exception e) { // Can't do anything: let's put a default value text = Messages.MarkerStatsView_unknownProblem; CheckstyleLog.log(e); } return text; } /** * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) */ public Image getColumnImage(Object obj, int index) { Image image = null; IMarker marker = (IMarker) obj; if (index == 0) { int severity = MarkerUtilities.getSeverity(marker); ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); if (IMarker.SEVERITY_ERROR == severity) { image = sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK); } else if (IMarker.SEVERITY_WARNING == severity) { image = sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK); } else if (IMarker.SEVERITY_INFO == severity) { image = sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK); } } return image; } public Comparable getComparableValue(Object element, int colIndex) { IMarker marker = (IMarker) element; Comparable comparable = null; switch (colIndex) { case 0: comparable = new Integer(marker.getAttribute(IMarker.SEVERITY, Integer.MAX_VALUE) * -1); break; case 1: comparable = marker.getResource().getName(); break; case 2: comparable = marker.getResource().getParent().getFullPath().toString(); break; case 3: comparable = new Integer(marker.getAttribute(IMarker.LINE_NUMBER, Integer.MAX_VALUE)); break; case 4: comparable = marker.getAttribute(IMarker.MESSAGE, "").toString(); break; default: comparable = ""; //$NON-NLS-1$ break; } return comparable; } public IDialogSettings getTableSettings() { IDialogSettings mainSettings = getDialogSettings(); IDialogSettings settings = mainSettings.getSection(TAG_SECTION_DETAIL); if (settings == null) { settings = mainSettings.addNewSection(TAG_SECTION_DETAIL); } return settings; } } }