/******************************************************************************* * Copyright (c) 2010 Freescale Semiconductor. * 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: * Serge Beauchamp (Freescale Semiconductor) - initial API and implementation *******************************************************************************/ package com.freescale.deadlockpreventer.agent; import java.io.CharArrayWriter; import java.io.IOException; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.viewers.CellLabelProvider; import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.Clipboard; import org.eclipse.swt.dnd.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.swt.IFocusService; import com.freescale.deadlockpreventer.ILock; import com.freescale.deadlockpreventer.Logger; import com.freescale.deadlockpreventer.QueryService.ITransaction; public class StatisticsDialog extends Dialog { static public class Row { public Row(int index, ILock lock) { this.index = index; id = lock.getID(); precedentsCount = lock.getPrecedents().length; folllowersCount = lock.getFollowers().length; location = lock.getStackTrace().length > 0 ? lock.getStackTrace()[0]:""; } int index; String id; int precedentsCount; int folllowersCount; String location; } public static Row[] convert(int startIndex, ILock[] tmp) { Row[] result = new Row[tmp.length]; for (int i = 0; i < tmp.length; i++) result[i] = new Row(startIndex + i, tmp[i]); return result; } Row[] locks; TableViewer viewer; TableViewerComparator comparator; ITransaction transaction; static StatisticsDialog sInstance = null; protected StatisticsDialog(Shell parentShell, Row[] locks, ITransaction transaction) { super(parentShell); this.locks = locks; this.transaction = transaction; sInstance = this; } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("Synchronization Primitives"); } protected int getShellStyle() { return SWT.CLOSE | SWT.MIN | SWT.MAX | SWT.RESIZE; } @Override protected Point getInitialSize() { Point pt = super.getInitialSize(); pt.x = Math.min(pt.x, 600); pt.y = Math.min(pt.y, 600); return pt; } protected void createButtonsForButtonBar(Composite parent) { // create OK and Cancel buttons by default createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); } @Override public boolean close() { transaction.close(); sInstance = null; return super.close(); } @Override protected Control createDialogArea(Composite parent) { parent.setLayout(new GridLayout(2, false)); viewer = new TableViewer(parent, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true); layoutData.horizontalSpan = 2; viewer.getTable().setLayoutData( layoutData); viewer.setContentProvider(new ViewContentProvider()); ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE); viewer.setInput(locks); IFocusService service = (IFocusService) PlatformUI.getWorkbench().getService(IFocusService.class); service.addFocusTracker(viewer.getTable(), StatisticsDialog.class.getPackage().getName() + ".table"); viewer.setLabelProvider(new CellLabelProvider() { @Override public void update(ViewerCell cell) { Row element = (Row) cell.getElement(); if (cell.getColumnIndex() == 0) cell.setText(element.id); if (cell.getColumnIndex() == 1) cell.setText(Integer.toString(element.folllowersCount)); if (cell.getColumnIndex() == 2) cell.setText(Integer.toString(element.precedentsCount)); if (cell.getColumnIndex() == 3) cell.setText(element.location); } public String getToolTipText(Object element) { Row row = (Row) element; ILock[] locks = transaction.getLocks(row.index, row.index + 1); CharArrayWriter writer = new CharArrayWriter(); Logger.dumpLockInformation(locks, writer); return writer.toString(); } public Point getToolTipShift(Object object) { return new Point(5, 5); } public int getToolTipDisplayDelayTime(Object object) { return 2000; } public int getToolTipTimeDisplayed(Object object) { return 5000; } }); createTableViewerColumn("Lock", 200, 0); createTableViewerColumn("Followers", 70, 1); createTableViewerColumn("Precedents", 70, 2); createTableViewerColumn("Location", 250, 3); viewer.getTable().setHeaderVisible(true); viewer.getTable().setLinesVisible(true); comparator = new TableViewerComparator(); viewer.setComparator(comparator); Button button = new Button(parent, SWT.PUSH); button.setText("Export..."); layoutData = new GridData(SWT.BEGINNING, SWT.TOP, false, false); layoutData.widthHint = 80; button.setLayoutData(layoutData); button.addSelectionListener( new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { StatisticsUtil.export(transaction); } }); Label label = new Label(parent, 0); label.setText("Total locks: " + locks.length); label.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false)); return super.createDialogArea(parent); } private TableViewerColumn createTableViewerColumn(String title, int bound, final int colNumber) { final TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE); final TableColumn column = viewerColumn.getColumn(); column.setText(title); column.setWidth(bound); column.setResizable(true); column.setMoveable(true); column.addSelectionListener(getSelectionAdapter(column, colNumber)); viewerColumn.setLabelProvider(new CellLabelProvider() { @Override public void update(ViewerCell cell) { Row element = (Row) cell.getElement(); switch(colNumber) { case 0: cell.setText(element.id); break; case 1: cell.setText(Integer.toString(element.folllowersCount)); break; case 2: cell.setText(Integer.toString(element.precedentsCount)); break; case 3: cell.setText(element.location); break; } } public String getToolTipText(Object element) { Row row = (Row) element; ILock[] locks = transaction.getLocks(row.index, row.index + 1); CharArrayWriter writer = new CharArrayWriter(); try { for (String stack : locks[0].getStackTrace()) { writer.write(stack + "\n"); } } catch (IOException e) { } return writer.toString(); } }); return viewerColumn; } private SelectionAdapter getSelectionAdapter(final TableColumn column, final int index) { SelectionAdapter selectionAdapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { comparator.setColumn(index); int dir = viewer.getTable().getSortDirection(); if (viewer.getTable().getSortColumn() == column) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { dir = SWT.DOWN; } viewer.getTable().setSortDirection(dir); viewer.getTable().setSortColumn(column); viewer.refresh(); } }; return selectionAdapter; } public class TableViewerComparator extends ViewerComparator { private int propertyIndex; private static final int DESCENDING = 1; private int direction = DESCENDING; public TableViewerComparator() { this.propertyIndex = 0; direction = DESCENDING; } public void setColumn(int column) { if (column == this.propertyIndex) { // Same column as last sort; toggle the direction direction = 1 - direction; } else { // New column; do an ascending sort this.propertyIndex = column; direction = DESCENDING; } } @Override public int compare(Viewer viewer, Object e1, Object e2) { Row p1 = (Row) e1; Row p2 = (Row) e2; int rc = 0; switch (propertyIndex) { case 0: rc = p1.id.compareTo(p2.id); break; case 1: rc = p1.folllowersCount - p2.folllowersCount; break; case 2: rc = p1.precedentsCount - p2.precedentsCount; break; case 3: rc = p1.location.compareTo(p2.location); break; default: rc = 0; } // If descending order, flip the direction if (direction == DESCENDING) { rc = -rc; } return rc; } } class ViewContentProvider implements IStructuredContentProvider, ITreeContentProvider { public void inputChanged(Viewer v, Object oldInput, Object newInput) { } public void dispose() { } public Object[] getElements(Object parent) { return getChildren(parent); } public Object getParent(Object child) { if (child instanceof ILock) return locks; return null; } public Object[] getChildren(Object parent) { if (parent == locks) return locks; return new Object[0]; } public boolean hasChildren(Object parent) { if (parent == locks) return true; return false; } } public static void copyCurrentRow() { if (sInstance != null) { sInstance.copySelection(); } } private void copySelection() { CharArrayWriter writer = new CharArrayWriter(); for (Object selectedRow : ((IStructuredSelection) viewer.getSelection()).toList()) { Row row = (Row) selectedRow; ILock[] locks = transaction.getLocks(row.index, row.index + 1); Logger.dumpLockInformation(locks, writer); } Clipboard cb = new Clipboard(Display.getDefault()); TextTransfer textTransfer = TextTransfer.getInstance(); cb.setContents(new Object[] { writer.toString().replace('\n', System.getProperty("line.separator").charAt(0)) }, new Transfer[] { textTransfer }); } }