blob: 96c440abcfb20384a326cbd87fbc308d7efb1ec1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Christian Pontesegger and others.
* 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:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.lang.unittest.ui.editor;
import java.util.Collection;
import java.util.HashSet;
import java.util.regex.Pattern;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.ease.lang.unittest.UnitTestHelper;
import org.eclipse.ease.lang.unittest.definition.IDefinitionPackage;
import org.eclipse.ease.lang.unittest.definition.ITestSuiteDefinition;
import org.eclipse.ease.lang.unittest.definition.IVariable;
import org.eclipse.ease.lang.unittest.runtime.ITestSuite;
import org.eclipse.ease.ui.tools.AbstractVirtualTreeProvider;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.ecore.util.EContentAdapter;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
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.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.part.ResourceTransfer;
import org.eclipse.ui.progress.UIJob;
/**
* Represents the Variables component in the Test Suite Editor. This class implements the Variables component in Test Suite Editor with a tree structure
* allowing to classify variables in groups.
*/
public class VariablesPage extends AbstractEditorPage {
private class VariablesTreeContentProvider extends AbstractVirtualTreeProvider {
/** Paths added by the user which are currently not populated by a variable. */
private final Collection<IPath> fAdditionalPaths = new HashSet<>();
/**
* Registers elements in the tree viewer from the test suites variables component.
*
* @param inputElement
* List of Variables defined for the test suite to be registered in the tree viewer.
*/
@Override
protected void populateElements(Object inputElement) {
setShowRoot(false);
if (inputElement instanceof ITestSuiteDefinition) {
for (final Object element : ((ITestSuiteDefinition) inputElement).getVariables()) {
if (element instanceof IVariable)
registerElement(((IVariable) element).getPath(), element);
}
}
for (final IPath additionalPath : fAdditionalPaths)
registerPath(additionalPath);
}
public void addPath(IPath path) {
fAdditionalPaths.add(path.makeAbsolute());
registerPath(path);
}
public void removePath(IPath path) {
fAdditionalPaths.remove(path.makeAbsolute());
}
public boolean containsPath(IPath path) {
return fAdditionalPaths.contains(path.makeAbsolute());
}
}
private static final String VARIABLES_EDITOR_ID = "org.eclipse.ease.editor.suiteEditor.variables";
private static final Pattern VARIABLE_NAME_PATTERN = Pattern.compile("([a-zA-Z_$][0-9a-zA-Z_$]*)");
/**
* Verify that a variables name is valid regarding coding rules.
*
* @param name
* name to be used
* @return <code>true</code> when new name is valid
*/
private static boolean isValidNamePattern(final String name) {
return VARIABLE_NAME_PATTERN.matcher(name).matches();
}
/** Job triggered on changes on the underlying EMF model. */
private final UIJob fUpdateUIJob = new UIJob("Update variables") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
fTreeViewer.refresh();
return Status.OK_STATUS;
}
};
private TreeViewer fTreeViewer;
public VariablesPage(final FormEditor editor, final String id, final String title) {
super(editor, id, title);
}
@Override
protected void createFormContent(final IManagedForm managedForm) {
super.createFormContent(managedForm);
managedForm.getForm().getBody().setLayout(new GridLayout(1, false));
final Label lblDefineVariablesThat = new Label(managedForm.getForm().getBody(), SWT.NONE);
managedForm.getToolkit().adapt(lblDefineVariablesThat, true, true);
lblDefineVariablesThat.setText("Define variables that will be visible in your scripts.");
final Composite composite = new Composite(managedForm.getForm().getBody(), SWT.NONE);
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
managedForm.getToolkit().adapt(composite);
managedForm.getToolkit().paintBordersFor(composite);
final TreeColumnLayout tcl_composite = new TreeColumnLayout();
composite.setLayout(tcl_composite);
final Tree tree = new Tree(composite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
fTreeViewer = new TreeViewer(tree);
tree.setHeaderVisible(true);
tree.setLinesVisible(true);
managedForm.getToolkit().paintBordersFor(tree);
// enable tooltip support
ColumnViewerToolTipSupport.enableFor(fTreeViewer);
fTreeViewer.setContentProvider(new VariablesTreeContentProvider());
fTreeViewer.setComparator(new ViewerComparator() {
@Override
public int compare(final Viewer viewer, final Object e1, final Object e2) {
if ((e1 instanceof IVariable) && (e2 instanceof IVariable))
return (((IVariable) e1).getName()).compareToIgnoreCase(((IVariable) e2).getName());
return super.compare(viewer, e1, e2);
}
@Override
public int category(Object element) {
return (element instanceof IPath) ? 1 : 2;
}
});
final TreeViewerColumn treeViewerColumn = new TreeViewerColumn(fTreeViewer, SWT.NONE);
final TreeColumn tblclmnVariable = treeViewerColumn.getColumn();
tcl_composite.setColumnData(tblclmnVariable, new ColumnWeightData(2, ColumnWeightData.MINIMUM_WIDTH, true));
tblclmnVariable.setText("Variable");
treeViewerColumn.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getName();
if (element instanceof IPath)
return ((IPath) element).lastSegment();
return super.getText(element);
}
@Override
public Image getImage(final Object element) {
if (element instanceof IVariable)
return DebugUITools.getImage(IDebugUIConstants.IMG_VIEW_VARIABLES);
else if (element instanceof IPath)
return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
return super.getImage(element);
}
@Override
public String getToolTipText(Object element) {
if (element instanceof IVariable) {
final String description = ((IVariable) element).getDescription();
if (description != null)
return description.replaceAll("\\\\n", "\n");
}
return super.getToolTipText(element);
}
});
treeViewerColumn.setEditingSupport(new EditingSupport(fTreeViewer) {
@Override
protected void setValue(Object element, final Object value) {
if (element instanceof IVariable) {
final IPath newPath = new Path(value.toString());
if (newPath.segmentCount() == 1) {
// simple name provided
final String oldName = ((IVariable) element).getName();
final String newName = value.toString();
if (!oldName.equals(newName)) {
// check for update
if ((isValidNamePattern(newName)) && (isUniqueName(newName))) {
final IPath newFullName = ((IVariable) element).getPath().append(newName);
final Command command = SetCommand.create(getEditingDomain(), element, IDefinitionPackage.Literals.VARIABLE__FULL_NAME,
newFullName);
getEditor().executeCommand(command);
fTreeViewer.update(element, null);
}
}
} else {
// path provided
final String newName = newPath.lastSegment();
if ((isValidNamePattern(newName)) && ((isUniqueName(newName)) || (((IVariable) element).getName().equals(newName)))) {
final IPath newFullName = (newPath.isAbsolute()) ? newPath : ((IVariable) element).getPath().append(newPath);
final Command command = SetCommand.create(getEditingDomain(), element, IDefinitionPackage.Literals.VARIABLE__FULL_NAME,
newFullName);
getEditor().executeCommand(command);
fTreeViewer.refresh();
}
}
} else if (element instanceof IPath) {
final IPath newPath = ((IPath) element).removeLastSegments(1).append(value.toString()).makeAbsolute();
if (!element.equals(newPath)) {
final CompoundCommand compoundCommand = new CompoundCommand();
for (final IVariable variable : getTestSuiteDefinition().getVariables()) {
if (((IPath) element).isPrefixOf(variable.getFullName())) {
final IPath updatedName = newPath.append(variable.getFullName().removeFirstSegments(((IPath) element).segmentCount()));
final Command command = SetCommand.create(getEditingDomain(), variable, IDefinitionPackage.Literals.VARIABLE__FULL_NAME,
updatedName);
compoundCommand.append(command);
}
}
if (!compoundCommand.isEmpty())
getEditor().executeCommand(compoundCommand);
// deal with pure virtual paths
removePath((IPath) element);
addPath(newPath);
fTreeViewer.refresh();
}
}
}
@Override
protected Object getValue(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getName();
else if (element instanceof IPath)
return ((IPath) element).lastSegment();
return "";
}
@Override
protected CellEditor getCellEditor(final Object element) {
return new TextCellEditor(fTreeViewer.getTree());
}
@Override
protected boolean canEdit(final Object element) {
return true;
}
});
final TreeViewerColumn treeViewerColumn_1 = new TreeViewerColumn(fTreeViewer, SWT.NONE);
final TreeColumn tblclmnContent = treeViewerColumn_1.getColumn();
tcl_composite.setColumnData(tblclmnContent, new ColumnWeightData(2, ColumnWeightData.MINIMUM_WIDTH, true));
tblclmnContent.setText("Content");
treeViewerColumn_1.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getContent();
if (element instanceof IPath)
return "";
return super.getText(element);
}
});
treeViewerColumn_1.setEditingSupport(new EditingSupport(fTreeViewer) {
@Override
protected void setValue(final Object element, final Object value) {
if (element instanceof IVariable) {
final String oldContent = ((IVariable) element).getContent();
final String newContent = value.toString();
if (!oldContent.equals(newContent)) {
final Command command = SetCommand.create(getEditingDomain(), element, IDefinitionPackage.Literals.VARIABLE__CONTENT, newContent);
getEditor().executeCommand(command);
fTreeViewer.update(element, null);
}
}
}
@Override
protected Object getValue(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getContent();
return "";
}
@Override
protected CellEditor getCellEditor(final Object element) {
return new TextCellEditor(fTreeViewer.getTree());
}
@Override
protected boolean canEdit(final Object element) {
return (element instanceof IVariable);
}
});
final TreeViewerColumn treeViewerColumn_3 = new TreeViewerColumn(fTreeViewer, SWT.NONE);
final TreeColumn tblclmnDescription = treeViewerColumn_3.getColumn();
tcl_composite.setColumnData(tblclmnDescription, new ColumnWeightData(3, ColumnWeightData.MINIMUM_WIDTH, true));
tblclmnDescription.setText("Description");
treeViewerColumn_3.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getDescription();
if (element instanceof IPath)
return "";
return super.getText(element);
}
});
treeViewerColumn_3.setEditingSupport(new EditingSupport(fTreeViewer) {
@Override
protected void setValue(final Object element, final Object value) {
if (element instanceof IVariable) {
final String oldDescription = ((IVariable) element).getDescription();
final String newDescription = value.toString();
if (!oldDescription.equals(newDescription)) {
final Command command = SetCommand.create(getEditingDomain(), element, IDefinitionPackage.Literals.VARIABLE__DESCRIPTION,
newDescription);
getEditor().executeCommand(command);
fTreeViewer.update(element, null);
}
}
}
@Override
protected Object getValue(final Object element) {
if (element instanceof IVariable)
return ((IVariable) element).getDescription();
return "";
}
@Override
protected CellEditor getCellEditor(final Object element) {
return new TextCellEditor(fTreeViewer.getTree());
}
@Override
protected boolean canEdit(final Object element) {
return (element instanceof IVariable);
}
});
initializeDnD();
final MenuManager contextMenu = new MenuManager("", VARIABLES_EDITOR_ID);
final Menu menu = contextMenu.createContextMenu(fTreeViewer.getTree());
contextMenu.setRemoveAllWhenShown(true);
fTreeViewer.getTree().setMenu(menu);
getEditorSite().registerContextMenu(VARIABLES_EDITOR_ID, contextMenu, fTreeViewer, false);
populateContent();
// adapter triggering when variables are added or removed (eg from context menu commands)
getTestSuiteDefinition().eAdapters().add(new EContentAdapter() {
@Override
public void notifyChanged(Notification notification) {
super.notifyChanged(notification);
fUpdateUIJob.schedule(100);
}
@Override
public boolean isAdapterForType(Object type) {
return (type instanceof ITestSuite);
}
});
getSite().setSelectionProvider(fTreeViewer);
}
@Override
protected void populateContent() {
fTreeViewer.setInput(getTestSuiteDefinition());
fTreeViewer.refresh();
fTreeViewer.expandAll();
}
@Override
protected String getPageTitle() {
return "Variables";
}
/**
* Provides drag and drop functionality for the tree structure.
*/
private void initializeDnD() {
final int operations = DND.DROP_MOVE;
final Transfer[] transferTypes = new Transfer[] { ResourceTransfer.getInstance(), LocalSelectionTransfer.getTransfer() };
fTreeViewer.addDragSupport(operations, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DragSourceAdapter() {
@Override
public void dragFinished(DragSourceEvent event) {
LocalSelectionTransfer.getTransfer().setSelection(null);
}
@Override
public void dragStart(DragSourceEvent event) {
LocalSelectionTransfer.getTransfer().setSelection(fTreeViewer.getSelection());
}
});
final ViewerDropAdapter dropSupport = new ViewerDropAdapter(fTreeViewer) {
@Override
public boolean validateDrop(Object target, int operation, TransferData transferData) {
for (final Transfer transferType : transferTypes)
if (transferType.isSupportedType(transferData))
return true;
return false;
}
@Override
public boolean performDrop(Object data) {
if (data instanceof IStructuredSelection) {
final Object file = ((IStructuredSelection) data).getFirstElement();
if (file instanceof IFile) {
if (!file.equals(getFile())) {
// drop of other suite file
try {
final ITestSuiteDefinition droppedDefinition = UnitTestHelper.loadTestSuite(((IFile) file).getContents());
final CompoundCommand compoundCommand = new CompoundCommand();
for (final IVariable variable : droppedDefinition.getVariables()) {
final IVariable existingVariable = getTestSuiteDefinition().getVariable(variable.getName());
if (existingVariable != null) {
final Command command = SetCommand.create(getEditingDomain(), existingVariable,
IDefinitionPackage.Literals.VARIABLE__CONTENT, variable.getContent());
compoundCommand.append(command);
} else {
final Command command = AddCommand.create(getEditingDomain(), getTestSuiteDefinition(),
IDefinitionPackage.Literals.TEST_SUITE_DEFINITION__VARIABLES, variable);
compoundCommand.append(command);
}
}
getEditor().executeCommand(compoundCommand);
fTreeViewer.refresh();
} catch (final Exception e) {
// not a valid content, fail DnD operation gracefully
return false;
}
}
} else {
// drop of variable or folder
Object target = getCurrentTarget();
if (target == null)
target = Path.ROOT;
if (target instanceof IPath) {
final CompoundCommand compoundCommand = new CompoundCommand();
for (final Object element : ((IStructuredSelection) data).toList()) {
if (element instanceof IVariable) {
final IPath newFullName = ((IPath) target).append(((IVariable) element).getName());
final Command command = SetCommand.create(getEditingDomain(), element, IDefinitionPackage.Literals.VARIABLE__FULL_NAME,
newFullName);
compoundCommand.append(command);
} else if (element instanceof IPath) {
for (final IVariable variable : getTestSuiteDefinition().getVariables()) {
if (((IPath) element).isPrefixOf(variable.getFullName())) {
final IPath updatedName = ((IPath) target).append(variable.getName());
final Command command = SetCommand.create(getEditingDomain(), variable,
IDefinitionPackage.Literals.VARIABLE__FULL_NAME, updatedName);
compoundCommand.append(command);
}
}
}
}
getEditor().executeCommand(compoundCommand);
fTreeViewer.refresh();
return true;
}
}
}
return false;
}
};
fTreeViewer.addDropSupport(operations, transferTypes, dropSupport);
}
/**
* Verify that a variables name is unique.
*
* @param name
* name to be used
* @return <code>true</code> when name is unique (= currently not used)
*/
public boolean isUniqueName(final String name) {
for (final IVariable variable : getTestSuiteDefinition().getVariables()) {
if (name.equals(variable.getName()))
return false;
}
return true;
}
public void addPath(IPath path) {
((VariablesTreeContentProvider) fTreeViewer.getContentProvider()).addPath(path);
fTreeViewer.refresh();
}
public void removePath(IPath path) {
((VariablesTreeContentProvider) fTreeViewer.getContentProvider()).removePath(path);
fTreeViewer.refresh();
}
public boolean containsPath(IPath path) {
return ((VariablesTreeContentProvider) fTreeViewer.getContentProvider()).containsPath(path);
}
@Override
public Image getTitleImage() {
return DebugUITools.getImage(IDebugUIConstants.IMG_VIEW_VARIABLES);
}
@Override
protected Image getDefaultImage() {
return DebugUITools.getImage(IDebugUIConstants.IMG_VIEW_VARIABLES);
}
}