package damp.ekeko.snippets.gui; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; //import org.eclipse.birt.chart.model.Chart; //import org.eclipse.birt.chart.model.ChartWithAxes; //import org.eclipse.birt.chart.model.attribute.ChartDimension; //import org.eclipse.birt.chart.model.attribute.LegendItemType; //import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl; //import org.eclipse.birt.chart.model.component.Axis; //import org.eclipse.birt.chart.model.component.Series; //import org.eclipse.birt.chart.model.component.impl.SeriesImpl; //import org.eclipse.birt.chart.model.data.NumberDataSet; //import org.eclipse.birt.chart.model.data.SeriesDefinition; //import org.eclipse.birt.chart.model.data.impl.NumberDataElementImpl; //import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl; //import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl; //import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl; //import org.eclipse.birt.chart.model.type.LineSeries; //import org.eclipse.birt.chart.model.type.impl.LineSeriesImpl; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.text.TextViewer; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IEditorReference; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.ListDialog; import org.eclipse.ui.model.WorkbenchPartLabelProvider; import org.eclipse.ui.part.EditorPart; import baristaui.util.MarkerUtility; import clojure.lang.IFn; import damp.ekeko.gui.EkekoLabelProvider; import damp.ekeko.snippets.EkekoSnippetsPlugin; import damp.ekeko.snippets.data.TemplateGroup; /** * The recommendation editor provides the GUI to automatically generalize or refine a template group, given a set of desired matches * @author Tim */ public class RecommendationEditor extends EditorPart { public RecommendationEditor() { } public static final String ID = "damp.ekeko.snippets.gui.RecommendationEditor"; //$NON-NLS-1$ public static IFn FN_PROJECT_VALUE_IDENTIFIER; public static IFn FN_PROJECT_TUPLE_IDENTIFIER; public static IFn FN_IDENTIFIER_CORRESPONDING_PROJECT_VALUE; public static IFn FN_EVOLVE; private TemplateEditor inputTemplateEditor; private TemplateEditor linkedTemplateEditor; private ToolBar toolBar; private TableViewer matchesViewer; private Table matchesViewerTable; private int activeColumn = -1; private Table verifiedViewerTable; private Link linkStatus; private ToolItem toolitemAddFromTemplate; private ToolItem toolitemSetInputTemplate; private EkekoLabelProvider ekekoLabelProvider; private Set<Collection<Object>> desiredMatches = new HashSet<>(); private List<List<Object>> evolveResults = new ArrayList<List<Object>>(); private List<Object> bestTemplatePerGen = new ArrayList<Object>(); private ToolItem toolitemDeleteDesiredMatch; // Default options of the underlying genetic search algorithm; see search.clj/config-default for more information private String evolveConfig = ":max-generations 100\n" + ":population-size 50\n" + ":selection-weight 1/4\n" + ":mutation-weight 3/4\n" + ":crossover-weight 0/4\n" + ":fitness-weights [18/20 2/20 0/20]\n" + ":fitness-threshold 0.99\n" + ":output-dir nil\n" + ":partial-matching true\n" + ":quick-matching false\n" + ":tournament-rounds 7"; private String outputDir = ""; // Components of the fitness chart // private Series generationAxis; // private LineSeries f1Data; // private LineSeries partialData; private ChartCanvas canvasView; private TextViewer bestTemplateTextArea; private TableViewer resultsTableViewer; private boolean birtInstalled = false; @Override public void doSave(IProgressMonitor monitor) { } @Override public void doSaveAs() { } public static Object projectValueIdentifier(Object value) { return FN_PROJECT_VALUE_IDENTIFIER.invoke(value); } public static Object projectTupleIdentifier(Object value) { return FN_PROJECT_TUPLE_IDENTIFIER.invoke(value); } public static Object projectvalueForIdentifier(Object identifier) { return FN_IDENTIFIER_CORRESPONDING_PROJECT_VALUE.invoke(identifier); } public static Object evolve(Object inputTemplate, Object desiredMatches, RecommendationEditor gui, String config) { return FN_EVOLVE.invoke(inputTemplate, desiredMatches, gui, "{" + config + "}"); } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(input); setPartName(input.getName()); } @Override public boolean isDirty() { return false; } @Override public boolean isSaveAsAllowed() { return true; } @Override public void createPartControl(Composite parent) { if (Platform.getBundle("org.eclipse.birt") != null) { birtInstalled = true; } parent.setLayout(new GridLayout(2,true)); // **** Toolbar at the top of the editor toolBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT); toolBar.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 2, 1)); toolitemSetInputTemplate = new ToolItem(toolBar, SWT.NONE); toolitemSetInputTemplate.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onEditLink(); } }); toolitemSetInputTemplate.setImage(EkekoSnippetsPlugin.IMG_ADD); toolitemSetInputTemplate.setToolTipText("Set the input template that needs recommendations"); toolitemAddFromTemplate = new ToolItem(toolBar, SWT.NONE); toolitemAddFromTemplate.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onAddMatchesFromTemplate(); } }); toolitemAddFromTemplate.setImage(EkekoSnippetsPlugin.IMG_TEMPLATE_ADD); toolitemAddFromTemplate.setToolTipText("Add the matches of a template as desired matches"); toolitemDeleteDesiredMatch = new ToolItem(toolBar, SWT.NONE); toolitemDeleteDesiredMatch.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onDeleteFromDesiredMatches(); } }); toolitemDeleteDesiredMatch.setImage(EkekoSnippetsPlugin.IMG_TEMPLATE_DELETE); toolitemDeleteDesiredMatch.setToolTipText("Delete from desired matches"); ToolItem toolitemPopInspector = new ToolItem(toolBar, SWT.NONE); toolitemPopInspector.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { openPopulationInspector((IStructuredSelection) resultsTableViewer.getSelection()); } }); toolitemPopInspector.setImage(EkekoSnippetsPlugin.IMG_PROPERTIES); toolitemPopInspector.setToolTipText("Open inspector for the selected generation"); ToolItem toolitemConfig = new ToolItem(toolBar, SWT.NONE); toolitemConfig.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { onEditConfig(); } }); toolitemConfig.setImage(EkekoSnippetsPlugin.IMG_TRANSFORMATION); toolitemConfig.setToolTipText("Edit configuration"); final ToolItem toolitemEvolve = new ToolItem(toolBar, SWT.NONE); toolitemEvolve.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (inputTemplateEditor == null) { ErrorDialog.openError(Display.getCurrent().getActiveShell(), "No input template", null, new Status(IStatus.INFO, EkekoSnippetsPlugin.PLUGIN_ID, "Please choose an input template. The recommendation system will try to automatically improve this template.", null)); } else if (desiredMatches.isEmpty()) { ErrorDialog.openError(Display.getCurrent().getActiveShell(), "No desired matches", null, new Status(IStatus.INFO, EkekoSnippetsPlugin.PLUGIN_ID, "Please add one or more desired matches. These are required as the recommmendation system aims to find a template that produces these desired matches.", null)); } else { onEvolve(); toolitemEvolve.setEnabled(false); // TODO For now, cannot restart the genetic algorithm yet } } }); toolitemEvolve.setImage(EkekoSnippetsPlugin.IMG_RECOMMENDATION); toolitemEvolve.setToolTipText("Suggest suitable modifications to the input template"); linkStatus = new Link(parent, SWT.NONE); linkStatus.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { if(event.text.equals("input template")) { onEditLink(); } else { onRevealLinkedEditor(); } }}); SashForm sash = new SashForm(parent, SWT.VERTICAL); sash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1)); // **** Top component of the SashForm: List of desired matches matchesViewer = new TableViewer(sash, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI); matchesViewerTable = matchesViewer.getTable(); matchesViewerTable.setLinesVisible(true); matchesViewerTable.setHeaderVisible(true); matchesViewer.setContentProvider(new ArrayContentProvider()); matchesViewer.setInput(desiredMatches); matchesViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { ISelection selection = event.getSelection(); } }); // **** Bottom component of the sash form: Results of genetic algorithm TabFolder tabs = new TabFolder(sash, SWT.BOTTOM); // *** Sash: Results table + best template TabItem resultsTab = new TabItem(tabs, SWT.NULL); resultsTab.setText("Results"); SashForm resultSash = new SashForm(tabs, SWT.HORIZONTAL); resultSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 2)); resultsTab.setControl(resultSash); resultsTableViewer = new TableViewer(resultSash, SWT.BORDER | SWT.FULL_SELECTION); Table resultsTable = resultsTableViewer.getTable(); resultsTable.setLinesVisible(true); resultsTable.setHeaderVisible(true); resultsTableViewer.setContentProvider(new ArrayContentProvider()); resultsTableViewer.setInput(evolveResults); TableViewerColumn[] cols = new TableViewerColumn[4]; cols[0] = addColumn(resultsTableViewer, 0, "Gen", 35); cols[1] = addColumn(resultsTableViewer, 1, "Best Fitness", 100); cols[2] = addColumn(resultsTableViewer, 2, "Best F1", 100); cols[3] = addColumn(resultsTableViewer, 3, "Best Partial", 100); for (int i=0; i< cols.length; i++) { final int idx = i; cols[i].setLabelProvider(new ColumnLabelProvider() { public String getText(Object element) { return ekekoLabelProvider.getText(nth((Collection) element, idx)); } }); } resultsTableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { openPopulationInspector((IStructuredSelection)event.getSelection()); } }); resultsTableViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { IStructuredSelection selection = (IStructuredSelection)event.getSelection(); int i = evolveResults.indexOf(selection.getFirstElement()); updateBestTemplate(bestTemplatePerGen.get(i)); } }); // ** Best template bestTemplateTextArea = new TextViewer(resultSash, SWT.BORDER); bestTemplateTextArea.setEditable(false); bestTemplateTextArea.getTextWidget().setFont(EkekoSnippetsPlugin.getEditorFont()); // *** Fitness chart TabItem fitnessTab = new TabItem(tabs, SWT.NULL); fitnessTab.setText("Fitness chart"); canvasView = new ChartCanvas(tabs, SWT.NO_BACKGROUND); //canvasView.setChart(chart); fitnessTab.setControl(canvasView); addActiveColumnListener(matchesViewerTable); addMenu(matchesViewer); ekekoLabelProvider = new EkekoLabelProvider(); linkToEditor(null); updateLinkWidget(); } /** * To be called by the evolve function (in search.clj) on each generation */ public void onNewGeneration(final Integer generation, final Double bestFitness, final Double bestF1, final Double bestPartial, final Object bestTemplate, final String outputDir) { // This method will be called from a separate thread. // We need to make sure all GUI changes happen on the SWT event thread using asyncExec. Display.getDefault().asyncExec(new Runnable() { public void run() { RecommendationEditor.this.outputDir = outputDir; addToChart(generation, bestF1, bestPartial); addToTable(generation, bestFitness, bestF1, bestPartial); bestTemplatePerGen.add(bestTemplate); updateBestTemplate(bestTemplate); } }); } private void updateBestTemplate(Object clojureTemplateGroup) { TemplateGroup templateGroup = TemplateGroup.newFromClojureGroup(clojureTemplateGroup); TemplatePrettyPrinter pp = new TemplatePrettyPrinter(templateGroup); String printed = pp.prettyPrint(); bestTemplateTextArea.getTextWidget().setText(printed); for(StyleRange range : pp.getStyleRanges()) bestTemplateTextArea.getTextWidget().setStyleRange(range); } private void addToTable(Integer generation, Double bestFitness, Double bestF1, Double bestPartial) { ArrayList<Object> newRow = new ArrayList<>(); newRow.add(generation); newRow.add(bestFitness); newRow.add(bestF1); newRow.add(bestPartial); evolveResults.add(newRow); resultsTableViewer.setInput(evolveResults); } // private Chart createChart() { // ChartWithAxes chart = ChartWithAxesImpl.create(); // chart.setDimension(ChartDimension.TWO_DIMENSIONAL_LITERAL); // chart.getPlot().setBackground(ColorDefinitionImpl.WHITE()); // chart.getLegend().setItemType(LegendItemType.SERIES_LITERAL); // chart.getLegend().setVisible(true); // chart.getTitle().setVisible(false); // // Axis xAxis = ((ChartWithAxes) chart).getPrimaryBaseAxes()[0]; // xAxis.getTitle().setVisible(true); // xAxis.getTitle().getCaption().setValue("Generation"); // // Axis yAxis = ((ChartWithAxes) chart).getPrimaryOrthogonalAxis(xAxis); // yAxis.getTitle().setVisible(false); // yAxis.getScale().setStep(0.2); // yAxis.getScale().setMin(NumberDataElementImpl.create(0.0)); // yAxis.getScale().setMax(NumberDataElementImpl.create(1.0)); // // NumberDataSet xValues = NumberDataSetImpl.create(new Integer[]{}); // generationAxis = SeriesImpl.create(); // generationAxis.setDataSet(xValues); // SeriesDefinition sdX = SeriesDefinitionImpl.create(); // sdX.getSeriesPalette().update(1); // xAxis.getSeriesDefinitions().add(sdX); // sdX.getSeries().add(generationAxis); // // NumberDataSet y1DataSet = NumberDataSetImpl.create(new Double[]{}); // f1Data = (LineSeries) LineSeriesImpl.create(); // f1Data.setDataSet(y1DataSet); // // NumberDataSet y2DataSet = NumberDataSetImpl.create(new Double[]{}); // partialData = (LineSeries) LineSeriesImpl.create(); // partialData.setDataSet(y2DataSet); // // SeriesDefinition sdY = SeriesDefinitionImpl.create(); // yAxis.getSeriesDefinitions().add(sdY); // f1Data.setSeriesIdentifier("F1 score"); // partialData.setSeriesIdentifier("Partial score"); // sdY.getSeries().add(f1Data); // sdY.getSeries().add(partialData); // // return chart; // // } private void addToChart(Integer generation, Double bestFitness, Double bestPartial) { if (!birtInstalled) { return; } // Chart chart = canvasView.getChart(); // if (chart==null) { // chart=createChart(); // canvasView.setChart(chart); // } // // NumberDataSetImpl xData = (NumberDataSetImpl)generationAxis.getDataSet(); // Integer[] rawVals = (Integer[])(xData.getValues()); // List<Integer> vals = new ArrayList<Integer>(Arrays.asList(rawVals)); // vals.add(generation); // generationAxis.setDataSet(NumberDataSetImpl.create(vals.toArray(new Integer[0]))); // // NumberDataSetImpl y1Data = (NumberDataSetImpl)f1Data.getDataSet(); // Double[] rawY1Vals = (Double[])(y1Data.getValues()); // List<Double> y1vals = new ArrayList<Double>(Arrays.asList(rawY1Vals)); // y1vals.add(bestFitness); // f1Data.setDataSet(NumberDataSetImpl.create(y1vals.toArray(new Double[0]))); // // NumberDataSetImpl y2Data = (NumberDataSetImpl)partialData.getDataSet(); // Double[] rawY2Vals = (Double[])(y2Data.getValues()); // List<Double> y2vals = new ArrayList<Double>(Arrays.asList(rawY2Vals)); // y2vals.add(bestPartial); // partialData.setDataSet(NumberDataSetImpl.create(y2vals.toArray(new Double[0]))); canvasView.redraw(); } protected void onDeleteFromDesiredMatches() { ISelection selection = matchesViewer.getSelection(); IStructuredSelection sel = (IStructuredSelection) selection; for (Object elem:sel.toArray()) { desiredMatches.remove(elem); } matchesViewer.refresh(); } protected void onEvolve() { evolve(inputTemplateEditor.getGroup().getGroup(), desiredMatches, this, evolveConfig); } protected void onEditConfig() { InputDialog dlg = new InputDialog(Display.getCurrent().getActiveShell(), "Edit configuration", "Recommendation configuration (See the documentation in search.clj)", evolveConfig, null) { protected int getInputTextStyle() { return SWT.MULTI | SWT.BORDER | SWT.V_SCROLL; } protected Control createDialogArea(Composite parent) { Control res = super.createDialogArea(parent); ((GridData) this.getText().getLayoutData()).heightHint = 160; return res; } }; int open = dlg.open(); if(open == dlg.OK) { evolveConfig = dlg.getValue(); } } protected void onAddMatchesFromTemplate() { // First pick a template or transformation ListDialog listSelectionDialog = new ListDialog(getSite().getShell()); listSelectionDialog.setContentProvider(new ArrayContentProvider()); listSelectionDialog.setLabelProvider(new WorkbenchPartLabelProvider()); listSelectionDialog.setInput(getTemplateEditors(true).toArray()); listSelectionDialog.setTitle("Retrieve desired matches from template"); listSelectionDialog.setMessage("Select a Ekeko/X template or transformation editor."); int open = listSelectionDialog.open(); if(open == listSelectionDialog.OK) { Object[] result = listSelectionDialog.getResult(); if(result.length == 1) { IEditorPart ed = ((IEditorPart) result[0]); if(ed instanceof TransformationEditor) { TransformationEditor transformationEditor = (TransformationEditor) ed; linkedTemplateEditor = transformationEditor.getSubjectsEditor(); } if(ed instanceof TemplateEditor) { linkedTemplateEditor = (TemplateEditor) ed; } } } // Create the columns for the table, if needed // for (TableColumn tableColumn : matchesViewerTable.getColumns()) { // tableColumn.dispose(); // } if (desiredMatches.isEmpty()) { for (Object object : getResultVariables()) { String varname = (String) object; final int columnIndex = matchesViewerTable.getColumnCount(); TableViewerColumn column = addColumn(matchesViewer, columnIndex, varname, 200); column.setLabelProvider(new ColumnLabelProvider() { public String getText(Object element) { return ekekoLabelProvider.getText(nth((Collection) element, columnIndex)); } }); } } else if (matchesViewerTable.getColumns().length != getResultVariables().size()) { ErrorDialog.openError(Display.getCurrent().getActiveShell(), "Incompatible template group", null, new Status(IStatus.WARNING, EkekoSnippetsPlugin.PLUGIN_ID, "Cannot use the matches from this template group, as its number of templates does not correspond.", null)); return; } // Add the matches to the table matchesViewerTable.layout(true); desiredMatches.addAll(getResults()); // desiredMatches = new HashSet(getResults()); matchesViewer.setInput(desiredMatches); } protected void onRevealLinkedEditor() { if(inputTemplateEditor != null) { PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().activate(inputTemplateEditor); } } private List<IEditorPart> getTemplateEditors(boolean includeTransformations) { IEditorReference[] editorReferences = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getEditorReferences(); LinkedList<IEditorPart> editors = new LinkedList<>(); for(IEditorReference ref : editorReferences) { IEditorPart editor = ref.getEditor(true); if(editor instanceof TemplateEditor) { editors.add(editor); } if(includeTransformations && editor instanceof TransformationEditor) { editors.add(editor); } } return editors; } protected void onEditLink() { ListDialog listSelectionDialog = new ListDialog(getSite().getShell()); listSelectionDialog.setContentProvider(new ArrayContentProvider()); listSelectionDialog.setLabelProvider(new WorkbenchPartLabelProvider()); listSelectionDialog.setInput(getTemplateEditors(false).toArray()); listSelectionDialog.setTitle("Select Input Template"); listSelectionDialog.setMessage("Select the Ekeko/X template for which recommendations are needed."); int open = listSelectionDialog.open(); if(open == listSelectionDialog.OK) { Object[] result = listSelectionDialog.getResult(); if(result.length == 1) { inputTemplateEditor = (TemplateEditor) result[0]; updateLinkWidget(); } } } private void updateLinkWidget() { if(inputTemplateEditor == null) { linkStatus.setText("No <a>input template</a> selected."); } else { linkStatus.setText("Input template: <a>" + inputTemplateEditor.getEditorInput().getName() + "</a>"); } linkStatus.pack(); } @SuppressWarnings("rawtypes") private Collection getResults() { return linkedTemplateEditor.getGroup().getResults(); } @SuppressWarnings("rawtypes") private Collection getResultVariables() { return linkedTemplateEditor.getGroup().getNormalizedMatchVariables(); } private void linkToEditor(IEditorPart editor) { if(editor instanceof TransformationEditor) { TransformationEditor transformationEditor = (TransformationEditor) editor; linkedTemplateEditor = transformationEditor.getSubjectsEditor(); } if(editor instanceof TemplateEditor) { linkedTemplateEditor = (TemplateEditor) editor; } if(editor == null) { linkedTemplateEditor = null; } updateLinkWidget(); } private void addMenu(final TableViewer tableViewer) { final Table table = tableViewer.getTable(); final MenuManager mgr = new MenuManager(); final Action revealNode = new Action("Reveal In Editor") { public void run() { revealNode(tableViewer, activeColumn); } }; mgr.setRemoveAllWhenShown(true); mgr.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager manager) { manager.add(revealNode); } }); table.setMenu(mgr.createContextMenu(table)); } protected void revealNode(TableViewer viewer, int activeColumn) { ISelection selection = viewer.getSelection(); IStructuredSelection sel = (IStructuredSelection) selection; if(sel.isEmpty()) return; Collection tuple = (Collection) sel.getFirstElement(); Object element = nth(tuple, activeColumn); if(element instanceof ASTNode) { ASTNode astNode = (ASTNode) element; MarkerUtility.getInstance().createMarkerAndGoto(astNode); } } private void addActiveColumnListener(final Table table) { table.addMouseListener(new MouseAdapter() { public void mouseDown(MouseEvent e) { int x = 0; for (int i = 0; i < table.getColumnCount(); i++) { x +=table.getColumn(i).getWidth(); if (e.x <= x) { activeColumn = i; break; } } } }); } private Object nth(Collection coll, int n) { Iterator iterator = coll.iterator(); for(int i=0; i<n; i++){ iterator.next(); } return iterator.next(); } protected TableViewerColumn addColumn(TableViewer viewer, final int columnIndex, String attributeName, int width) { TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE, columnIndex); column.getColumn().setWidth(width); column.getColumn().setText(attributeName); column.getColumn().setMoveable(true); return column; } @Override public void setFocus() {} private void openPopulationInspector(IStructuredSelection selection) { try { int i = evolveResults.indexOf(selection.getFirstElement()); PopulationInspectorDialog pi = new PopulationInspectorDialog( getSite().getShell(), outputDir, i); pi.open(); } catch (Exception e) { e.printStackTrace(); } } }