package damp.ekeko.snippets.gui;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
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.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
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.jface.viewers.Viewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
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.graphics.Color;
import org.eclipse.swt.graphics.Device;
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.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
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 clojure.lang.IFn;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import baristaui.util.MarkerUtility;
import damp.ekeko.gui.EkekoLabelProvider;
import damp.ekeko.snippets.EkekoSnippetsPlugin;
public class IntendedResultsEditor extends EditorPart {
public IntendedResultsEditor() {
}
public static final String ID = "damp.ekeko.snippets.gui.IntendedResultsEditor"; //$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;
private IEditorPart linkedTransformationOrTemplateEditor;
private TemplateEditor linkedTemplateEditor;
private ToolBar toolBar;
private TableViewer matchesViewer;
private Table matchesViewerTable;
private int activeColumn = -1;
private TableViewer verifiedViewer;
private Table verifiedViewerTable;
private Link linkStatus;
private Button linkButton;
private ToolItem toolitemCompareResults;
private EkekoLabelProvider ekekoLabelProvider;
private ToolItem toolitemAddPositive;
private ToolItem toolitemAddNegative;
private Set<Collection<Object>> results = new HashSet<>();
private Set<Object> positiveIDs = new HashSet<>();
private Set<Object> negativeIDs = new HashSet<>();
private IStructuredContentProvider verifiedContentProvider;
private ArrayContentProvider matchesContentProvider;
private ToolItem toolitemDeleteVerifiedResult;
@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);
}
@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) {
parent.setLayout(new GridLayout(2,true));
toolBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT);
toolBar.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 2, 1));
toolitemCompareResults = new ToolItem(toolBar, SWT.NONE);
toolitemCompareResults.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onCompareResults();
}
});
toolitemCompareResults.setImage(EkekoSnippetsPlugin.IMG_RESULTS_REFRESH);
toolitemCompareResults.setToolTipText("Compare results");
toolitemAddPositive = new ToolItem(toolBar, SWT.NONE);
toolitemAddPositive.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onAddPositiveExample();
}
});
toolitemAddPositive.setImage(EkekoSnippetsPlugin.IMG_POSITIVE_EXAMPLE);
toolitemAddPositive.setToolTipText("Mark as positive example");
toolitemAddNegative = new ToolItem(toolBar, SWT.NONE);
toolitemAddNegative.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onAddNegativeExample();
}
});
toolitemAddNegative.setImage(EkekoSnippetsPlugin.IMG_NEGATIVE_EXAMPLE);
toolitemAddNegative.setToolTipText("Mark as negative example");
final ToolItem tltmSearchModifications = new ToolItem(toolBar, SWT.NONE);
tltmSearchModifications.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onSearchModifications();
}
});
tltmSearchModifications.setImage(EkekoSnippetsPlugin.IMG_SEARCH);
tltmSearchModifications.setToolTipText("Suggest suitable modifications to template");
tltmSearchModifications.setEnabled(false);
linkStatus = new Link(parent, SWT.NONE);
linkStatus.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 1, 1));
linkStatus.addListener(SWT.Selection, new Listener() {
@Override
public void handleEvent(Event event) {
if(event.text.equals("linked")) {
onEditLink();
} else {
onRevealLinkedEditor();
}
}});
linkButton = new Button(parent, SWT.NONE);
linkButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false, 1, 1));
linkButton.setText("Link to editor...");
linkButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onEditLink();
}
});
SashForm sash = new SashForm(parent, SWT.VERTICAL);
sash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
matchesViewer = new TableViewer(sash, SWT.BORDER | SWT.FULL_SELECTION);
matchesViewerTable = matchesViewer.getTable();
matchesViewerTable.setLinesVisible(true);
matchesViewerTable.setHeaderVisible(true);
matchesViewer.setContentProvider(new ArrayContentProvider());
matchesContentProvider = new ArrayContentProvider();
matchesViewer.setContentProvider(matchesContentProvider);
matchesViewer.setInput(results);
matchesViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
toolitemAddNegative.setEnabled(!selection.isEmpty());
toolitemAddPositive.setEnabled(!selection.isEmpty());
}
});
//**** Bottom ****
Composite bottomComposite = new Composite(sash, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.marginWidth = 0;
gridLayout.marginHeight = 0;
bottomComposite.setLayout(gridLayout);
ToolBar bottomToolBar = new ToolBar(bottomComposite, SWT.FLAT | SWT.RIGHT);
bottomToolBar.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, true, false, 1, 1));
ToolItem toolitemInitialize = new ToolItem(bottomToolBar, SWT.NONE);
toolitemInitialize.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
//initialize from file, clone instances, recorded changes, diff ...
//probably want drop down menu
}
});
toolitemInitialize.setImage(EkekoSnippetsPlugin.IMG_RESULTS_IMPORT);
toolitemInitialize.setToolTipText("Initialize intended results");
ToolItem toolitemAddColumn = new ToolItem(bottomToolBar, SWT.NONE);
toolitemAddColumn.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
addColumnToVerifiedViewer(verifiedViewerTable.getColumnCount());
verifiedViewer.refresh();
}
});
toolitemAddColumn.setImage(EkekoSnippetsPlugin.IMG_COLUMN_ADD);
toolitemAddColumn.setToolTipText("Add Column");
toolitemDeleteVerifiedResult = new ToolItem(bottomToolBar, SWT.NONE);
toolitemDeleteVerifiedResult.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onDeleteVerifiedResult();
}
});
toolitemDeleteVerifiedResult.setImage(EkekoSnippetsPlugin.IMG_DELETE);
toolitemDeleteVerifiedResult.setToolTipText("Delete example");
verifiedViewer = new TableViewer(bottomComposite, SWT.BORDER | SWT.FULL_SELECTION);
verifiedViewerTable = verifiedViewer.getTable();
verifiedViewerTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
verifiedViewerTable.setLinesVisible(true);
verifiedViewerTable.setHeaderVisible(true);
TableViewerColumn imgColumn = new TableViewerColumn(verifiedViewer, SWT.NONE, 0);
imgColumn.getColumn().setWidth(24);
imgColumn.getColumn().setResizable(false);
imgColumn.getColumn().setText("");
imgColumn.getColumn().setMoveable(false);
imgColumn.setLabelProvider(new ColumnLabelProvider() {
@SuppressWarnings("rawtypes")
@Override
public String getText(Object element) {
return "";
}
@Override
public org.eclipse.swt.graphics.Image getImage(Object tupleIdentifier) {
if(isPositiveIdentifier(tupleIdentifier))
return EkekoSnippetsPlugin.IMG_POSITIVE_EXAMPLE;
if(isNegativeIdentifier(tupleIdentifier))
return EkekoSnippetsPlugin.IMG_NEGATIVE_EXAMPLE;
return null;
}
});
verifiedContentProvider = new ArrayContentProvider();
verifiedViewer.setContentProvider(verifiedContentProvider);
verifiedViewer.setInput(Sets.union(positiveIDs,negativeIDs));
verifiedViewer.addSelectionChangedListener(new ISelectionChangedListener() {
@Override
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
toolitemDeleteVerifiedResult.setEnabled(!selection.isEmpty());
}
});
addActiveColumnListener(matchesViewerTable);
addActiveColumnListener(verifiedViewerTable);
addMenu(matchesViewer);
addMenu(verifiedViewer);
ekekoLabelProvider = new EkekoLabelProvider();
linkToEditor(null);
updateWidgets();
}
protected void onDeleteVerifiedResult() {
ISelection selection = verifiedViewer.getSelection();
IStructuredSelection sel = (IStructuredSelection) selection;
if(sel.isEmpty()) {
return;
}
Object exampleID = sel.getFirstElement();
positiveIDs.remove(exampleID);
negativeIDs.remove(exampleID);
verifiedViewer.refresh();
}
protected boolean isPositiveIdentifier(Object tupleIdentifier) {
return positiveIDs.contains(tupleIdentifier);
}
protected void onSearchModifications() {
}
protected void addVerifiedExample(boolean isPositive) {
ISelection selection = matchesViewer.getSelection();
IStructuredSelection sel = (IStructuredSelection) selection;
if(sel.isEmpty()) {
return;
}
Collection selectedTuple = (Collection) sel.getFirstElement();
Object tupleIdentifier = projectTupleIdentifier(selectedTuple);
if(isPositive) {
positiveIDs.add(tupleIdentifier);
negativeIDs.remove(tupleIdentifier);
} else {
negativeIDs.add(tupleIdentifier);
positiveIDs.remove(tupleIdentifier);
}
//arraycontentprovider's implementation does nothing
//verifiedContentProvider.inputChanged(verifiedViewer, verifiedViewer.getInput(), Sets.union(positives, negatives));
//would be better to update only for the added example...
verifiedViewer.setInput(Sets.union(positiveIDs, negativeIDs));
matchesViewer.refresh();
}
protected void onAddPositiveExample() {
addVerifiedExample(true);
}
protected void onAddNegativeExample() {
addVerifiedExample(false);
}
protected void onCompareResults() {
for (TableColumn tableColumn : matchesViewerTable.getColumns()) {
tableColumn.dispose();
}
for (Object object : getResultVariables()) {
String varname = (String) object;
final int columnIndex = matchesViewerTable.getColumnCount();
TableViewerColumn column = addColumn(matchesViewer, columnIndex, varname);
/*
column.setEditingSupport(new EditingSupport(matchesViewer) {
private Object getCellValue(final Object element) {
Collection row = (Collection) element;
Object cellValue = nth(row, columnIndex);
return cellValue;
}
@Override
protected void setValue(Object element, Object value) {
}
@Override
protected Object getValue(Object element) {
return getCellValue(element);
}
@Override
protected CellEditor getCellEditor(final Object element) {
return new DialogCellEditor(matchesViewerTable) {
@Override
protected Object openDialogBox(Control cellEditorWindow) {
OpenASTNodeInEditorAction action = new OpenASTNodeInEditorAction(null);
try {
action.showNodeInEditor((ASTNode) getCellValue(element));
} catch (PartInitException | JavaModelException e) {
e.printStackTrace();
}
return null;
}
};
}
@Override
protected boolean canEdit(Object element) {
return getCellValue(element) instanceof ASTNode;
}
});
*/
column.setLabelProvider(new ColumnLabelProvider() {
@SuppressWarnings("rawtypes")
@Override
public String getText(Object element) {
return ekekoLabelProvider.getText(nth((Collection) element, columnIndex));
}
@Override
public Color getBackground(Object element) {
Collection tuple = (Collection) element;
if(isPositive(tuple))
return Display.getCurrent().getSystemColor(SWT.COLOR_GREEN);
if(isNegative(tuple))
return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
return super.getBackground(element);
}
});
}
matchesViewerTable.layout(true);
results = new HashSet(getResults());
//arraycontentprovider's implementation does nothing
//matchesContentProvider.inputChanged(matchesViewer, matchesViewer.getInput(), results);
matchesViewer.setInput(results);
}
protected void onRevealLinkedEditor() {
if(linkedTemplateEditor != null) {
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().activate(linkedTransformationOrTemplateEditor);
}
}
private List<IEditorPart> getTemplateEditors() {
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(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().toArray());
listSelectionDialog.setTitle("Select Ekeko/X editor");
listSelectionDialog.setMessage("Select the Ekeko/X template or transformation editor to link to.");
int open = listSelectionDialog.open();
if(open == listSelectionDialog.OK) {
Object[] result = listSelectionDialog.getResult();
if(result.length == 1) {
linkToEditor((IEditorPart) result[0]);
}
}
}
private void updateLinkWidget() {
if(linkedTransformationOrTemplateEditor instanceof TransformationEditor) {
linkStatus.setText("Linked to LHS of transformation editor on <a>" + linkedTransformationOrTemplateEditor.getEditorInput().getName() + "</a>");
}
if(linkedTransformationOrTemplateEditor instanceof TemplateEditor) {
linkStatus.setText("Linked to template editor on <a>" + linkedTransformationOrTemplateEditor.getEditorInput().getName() + "</a>");
}
if(linkedTransformationOrTemplateEditor == null) {
linkStatus.setText("Not <a>linked</a> to template editor.");
}
linkStatus.pack();
}
private void updateWidgets() {
updateLinkWidget();
toolitemCompareResults.setEnabled(linkedTransformationOrTemplateEditor != null);
if(linkedTemplateEditor == null) {
toolitemAddNegative.setEnabled(false);
toolitemAddPositive.setEnabled(false);
toolitemCompareResults.setEnabled(false);
} else {
toolitemCompareResults.setEnabled(true);
ISelection selection = matchesViewer.getSelection();
toolitemAddNegative.setEnabled(!selection.isEmpty());
toolitemAddPositive.setEnabled(!selection.isEmpty());
}
toolitemDeleteVerifiedResult.setEnabled(!verifiedViewer.getSelection().isEmpty());
}
@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();
linkedTransformationOrTemplateEditor = editor;
}
if(editor instanceof TemplateEditor) {
linkedTemplateEditor = (TemplateEditor) editor;
linkedTransformationOrTemplateEditor = editor;
}
if(editor == null) {
linkedTemplateEditor = null;
linkedTransformationOrTemplateEditor = null;
}
updateWidgets();
}
private void addMenu(final TableViewer tableViewer) {
final Table table = tableViewer.getTable();
final MenuManager mgr = new MenuManager();
final Action insertColumnAfter = new Action("Insert New Column After") {
public void run() {
addColumnToVerifiedViewer(activeColumn + 1);
verifiedViewer.refresh();
}
};
insertColumnAfter.setImageDescriptor(ImageDescriptor.createFromImage(EkekoSnippetsPlugin.IMG_COLUMN_ADD));
final Action removeColumn = new Action("Remove Column") {
public void run() {
removeColumn(activeColumn);
}
};
removeColumn.setImageDescriptor(ImageDescriptor.createFromImage(EkekoSnippetsPlugin.IMG_COLUMN_DELETE));
final Action revealNode = new Action("Reveal In Editor") {
public void run() {
revealNode(tableViewer, activeColumn);
}
};
//removeColumn.setImageDescriptor(ImageDescriptor.createFromImage(EkekoSnippetsPlugin.IMG_COLUMN_DELETE));
mgr.setRemoveAllWhenShown(true);
mgr.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
if(tableViewer.equals(verifiedViewer)) {
if (table.getColumnCount() == 1) {
manager.add(insertColumnAfter);
} else {
manager.add(insertColumnAfter);
manager.add(removeColumn);
}
}
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;
}
}
}
});
}
protected void removeColumn(int columnIndex) {
matchesViewerTable.getColumn(columnIndex).dispose();
matchesViewerTable.layout(true);
verifiedViewerTable.getColumn(columnIndex).dispose();
verifiedViewerTable.layout(true);
}
protected void addRow() {
}
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) {
TableViewerColumn column = new TableViewerColumn(viewer, SWT.NONE, columnIndex);
column.getColumn().setWidth(200);
column.getColumn().setText(attributeName);
column.getColumn().setMoveable(true);
return column;
}
protected TableViewerColumn addColumnToVerifiedViewer(final int columnIndex) {
String attributeName = getAttributeName();
if(attributeName == null)
return null;
TableViewerColumn col = addColumn(verifiedViewer, columnIndex, attributeName);
col.setLabelProvider(new ColumnLabelProvider() {
@SuppressWarnings("rawtypes")
@Override
public String getText(Object element) {
Collection tuple = (Collection) element;
Object identifier = nth(tuple, columnIndex - 1); //0th col is the +/- img
Object value = projectvalueForIdentifier(identifier);
return ekekoLabelProvider.getText(value);
}
});
return col;
}
public boolean isPositive(Collection<Object> tuple) {
Object tupleIdentifier = projectTupleIdentifier(tuple);
return isPositiveIdentifier(tupleIdentifier);
}
public boolean isNegative(Collection<Object> tuple) {
Object tupleIdentifier = projectTupleIdentifier(tuple);
return isNegativeIdentifier(tupleIdentifier);
}
private boolean isNegativeIdentifier(Object tupleIdentifier) {
return negativeIDs.contains(tupleIdentifier);
}
@Override
public void setFocus() {
//Object cljTransformation = transformationEditor.getTransformation();
//textViewerSnippet.getControl().setFocus();
}
public void setTemplateEditor(TemplateEditor templateEditor) {
this.linkedTemplateEditor = templateEditor;
}
protected String getAttributeName() {
InputDialog dlg = new InputDialog(Display.getCurrent().getActiveShell(),"Attribute Name", "Please enter a name for the new attribute", "?attribute", new IInputValidator() {
@Override
public String isValid(String newText) {
if(newText.length() < 1 ||
newText.charAt(0) != '?') {
return "Attribute name should start with '?'";
}
return null;
}
});
if (dlg.open() == Window.OK) {
return dlg.getValue();
}
else
return null;
}
public void setPreviouslyActiveEditor(IEditorPart activeEditor) {
}
}